@promptbook/wizard 0.112.0-99 → 0.113.0-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -44
- package/esm/index.es.js +1813 -103
- 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 +22 -2
- package/esm/src/_packages/node.index.d.ts +40 -0
- package/esm/src/_packages/types.index.d.ts +20 -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-2.0/agent-source/AgentBasicInformation.d.ts +2 -0
- package/esm/src/book-2.0/agent-source/agentSourceVisibility.d.ts +97 -0
- package/esm/src/book-2.0/agent-source/agentSourceVisibility.test.d.ts +1 -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 +2 -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 +23 -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/CreateAgentInput.d.ts +2 -1
- package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
- package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +21 -1
- package/esm/src/commitments/META_VISIBILITY/META_VISIBILITY.d.ts +27 -0
- package/esm/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
- package/esm/src/commitments/index.d.ts +2 -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 +1814 -101
- 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 +22 -2
- package/umd/src/_packages/node.index.d.ts +40 -0
- package/umd/src/_packages/types.index.d.ts +20 -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-2.0/agent-source/AgentBasicInformation.d.ts +2 -0
- package/umd/src/book-2.0/agent-source/agentSourceVisibility.d.ts +97 -0
- package/umd/src/book-2.0/agent-source/agentSourceVisibility.test.d.ts +1 -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 +2 -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 +23 -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/CreateAgentInput.d.ts +2 -1
- package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
- package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +21 -1
- package/umd/src/commitments/META_VISIBILITY/META_VISIBILITY.d.ts +27 -0
- package/umd/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
- package/umd/src/commitments/index.d.ts +2 -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/esm/index.es.js
CHANGED
|
@@ -7,20 +7,20 @@ import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
|
|
|
7
7
|
import { mkdir, rm, readFile, readdir, rename, rmdir, stat, access, constants, writeFile, watch, unlink } from 'fs/promises';
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
9
|
import { forTime } from 'waitasecond';
|
|
10
|
-
import
|
|
10
|
+
import CryptoJS from 'crypto-js';
|
|
11
11
|
import hexEncoder from 'crypto-js/enc-hex';
|
|
12
12
|
import { basename, join, dirname, isAbsolute, relative } from 'path';
|
|
13
13
|
import { Readability } from '@mozilla/readability';
|
|
14
14
|
import { JSDOM } from 'jsdom';
|
|
15
|
-
import
|
|
15
|
+
import showdown from 'showdown';
|
|
16
16
|
import * as dotenv from 'dotenv';
|
|
17
17
|
import sha256 from 'crypto-js/sha256';
|
|
18
18
|
import JSZip from 'jszip';
|
|
19
19
|
import { Subject, BehaviorSubject } from 'rxjs';
|
|
20
|
-
import
|
|
20
|
+
import 'crypto';
|
|
21
21
|
import moment from 'moment';
|
|
22
22
|
import { lookup, extension } from 'mime-types';
|
|
23
|
-
import
|
|
23
|
+
import papaparse from 'papaparse';
|
|
24
24
|
import { fileSearchTool, tool, Agent as Agent$1, webSearchTool, run, setDefaultOpenAIClient, setDefaultOpenAIKey } from '@openai/agents';
|
|
25
25
|
import OpenAI from 'openai';
|
|
26
26
|
|
|
@@ -38,7 +38,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
38
38
|
* @generated
|
|
39
39
|
* @see https://github.com/webgptorg/promptbook
|
|
40
40
|
*/
|
|
41
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.
|
|
41
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.113.0-0';
|
|
42
42
|
/**
|
|
43
43
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
44
44
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -2093,7 +2093,7 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
|
|
|
2093
2093
|
title: 'Anthropic Claude',
|
|
2094
2094
|
packageName: '@promptbook/anthropic-claude',
|
|
2095
2095
|
className: 'AnthropicClaudeExecutionTools',
|
|
2096
|
-
envVariables: ['ANTHROPIC_CLAUDE_API_KEY'],
|
|
2096
|
+
envVariables: ['ANTHROPIC_API_KEY', 'ANTHROPIC_CLAUDE_API_KEY'],
|
|
2097
2097
|
trustLevel: 'CLOSED',
|
|
2098
2098
|
order: MODEL_ORDERS.TOP_TIER,
|
|
2099
2099
|
getBoilerplateConfiguration() {
|
|
@@ -2110,14 +2110,17 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
|
|
|
2110
2110
|
};
|
|
2111
2111
|
},
|
|
2112
2112
|
createConfigurationFromEnv(env) {
|
|
2113
|
+
var _a;
|
|
2113
2114
|
// Note: Note using `process.env` BUT `env` to pass in the environment variables dynamically
|
|
2114
|
-
|
|
2115
|
+
// Note: `ANTHROPIC_API_KEY` takes precedence over the deprecated `ANTHROPIC_CLAUDE_API_KEY`
|
|
2116
|
+
const apiKey = (_a = env.ANTHROPIC_API_KEY) !== null && _a !== void 0 ? _a : env.ANTHROPIC_CLAUDE_API_KEY;
|
|
2117
|
+
if (typeof apiKey === 'string') {
|
|
2115
2118
|
return {
|
|
2116
2119
|
title: 'Claude (from env)',
|
|
2117
2120
|
packageName: '@promptbook/anthropic-claude',
|
|
2118
2121
|
className: 'AnthropicClaudeExecutionTools',
|
|
2119
2122
|
options: {
|
|
2120
|
-
apiKey
|
|
2123
|
+
apiKey,
|
|
2121
2124
|
},
|
|
2122
2125
|
};
|
|
2123
2126
|
}
|
|
@@ -2346,7 +2349,7 @@ class ParseError extends Error {
|
|
|
2346
2349
|
* @private internal helper function
|
|
2347
2350
|
*/
|
|
2348
2351
|
function $randomToken(randomness) {
|
|
2349
|
-
return
|
|
2352
|
+
return CryptoJS.lib.WordArray.random(randomness).toString(CryptoJS.enc.Hex);
|
|
2350
2353
|
}
|
|
2351
2354
|
// TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
|
|
2352
2355
|
// TODO: Maybe use nanoid instead https://github.com/ai/nanoid
|
|
@@ -10020,7 +10023,7 @@ class OpenAiVectorStoreKnowledgeSourcePreparer {
|
|
|
10020
10023
|
return { skippedReason: 'invalid_data_url' };
|
|
10021
10024
|
}
|
|
10022
10025
|
return {
|
|
10023
|
-
file: new File([parsed.buffer], parsed.filename, {
|
|
10026
|
+
file: new File([new Uint8Array(parsed.buffer)], parsed.filename, {
|
|
10024
10027
|
type: parsed.mimeType,
|
|
10025
10028
|
}),
|
|
10026
10029
|
sizeBytes: parsed.buffer.length,
|
|
@@ -13301,7 +13304,7 @@ function $registeredScrapersMessage(availableScrapers) {
|
|
|
13301
13304
|
* @public exported from `@promptbook/editable`
|
|
13302
13305
|
*/
|
|
13303
13306
|
function knowledgeSourceContentToName(knowledgeSourceContent) {
|
|
13304
|
-
const hash = SHA256(hexEncoder.parse(JSON.stringify(knowledgeSourceContent)))
|
|
13307
|
+
const hash = CryptoJS.SHA256(hexEncoder.parse(JSON.stringify(knowledgeSourceContent)))
|
|
13305
13308
|
// <- TODO: [🥬] Encapsulate sha256 to some private utility function
|
|
13306
13309
|
.toString( /* hex */)
|
|
13307
13310
|
.substring(0, 20);
|
|
@@ -14181,7 +14184,7 @@ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO:
|
|
|
14181
14184
|
console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
|
|
14182
14185
|
value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
14183
14186
|
}
|
|
14184
|
-
const csv = parse(value, settings);
|
|
14187
|
+
const csv = papaparse.parse(value, settings);
|
|
14185
14188
|
return csv;
|
|
14186
14189
|
}
|
|
14187
14190
|
|
|
@@ -14266,10 +14269,10 @@ const CsvFormatParser = {
|
|
|
14266
14269
|
i > index ? { ...row, [outputParameterName]: PENDING_VALUE_PLACEHOLDER } : row,
|
|
14267
14270
|
);
|
|
14268
14271
|
*/
|
|
14269
|
-
await onProgress(unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
|
|
14272
|
+
await onProgress(papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
|
|
14270
14273
|
}
|
|
14271
14274
|
}
|
|
14272
|
-
return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
14275
|
+
return papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
14273
14276
|
},
|
|
14274
14277
|
},
|
|
14275
14278
|
{
|
|
@@ -14297,7 +14300,7 @@ const CsvFormatParser = {
|
|
|
14297
14300
|
return /* not await */ mapCallback({ [key]: value }, index, array.length);
|
|
14298
14301
|
}));
|
|
14299
14302
|
}));
|
|
14300
|
-
return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
14303
|
+
return papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
14301
14304
|
},
|
|
14302
14305
|
},
|
|
14303
14306
|
],
|
|
@@ -16313,7 +16316,7 @@ async function getScraperIntermediateSource(source, options) {
|
|
|
16313
16316
|
const { filename: sourceFilename, url } = source;
|
|
16314
16317
|
const { rootDirname, cacheDirname, intermediateFilesStrategy, extension, isVerbose } = options;
|
|
16315
16318
|
// TODO: [👬] DRY
|
|
16316
|
-
const hash = SHA256(
|
|
16319
|
+
const hash = CryptoJS.SHA256(
|
|
16317
16320
|
// <- TODO: [🥬] Encapsulate sha256 to some private utility function
|
|
16318
16321
|
hexEncoder.parse(sourceFilename || url || 'untitled'))
|
|
16319
16322
|
.toString( /* hex */)
|
|
@@ -17216,7 +17219,7 @@ const _WebsiteScraperMetadataRegistration = $scrapersMetadataRegister.register(w
|
|
|
17216
17219
|
* @private utility of `WebsiteScraper`
|
|
17217
17220
|
*/
|
|
17218
17221
|
function createShowdownConverter() {
|
|
17219
|
-
return new Converter({
|
|
17222
|
+
return new showdown.Converter({
|
|
17220
17223
|
flavor: 'github',
|
|
17221
17224
|
/*
|
|
17222
17225
|
> import showdownHighlight from 'showdown-highlight';
|
|
@@ -17400,7 +17403,7 @@ function normalizeWhitespaces(sentence) {
|
|
|
17400
17403
|
* @public exported from `@promptbook/utils`
|
|
17401
17404
|
*/
|
|
17402
17405
|
function computeHash(value) {
|
|
17403
|
-
return SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
|
|
17406
|
+
return CryptoJS.SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
|
|
17404
17407
|
}
|
|
17405
17408
|
// TODO: [🥬][🥬] Use this ACRY
|
|
17406
17409
|
|
|
@@ -19169,6 +19172,11 @@ class FormatCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
19169
19172
|
* - `isValidAgentUrl` *(this one)* which tests just agent URL
|
|
19170
19173
|
* - `isValidPipelineUrl` which tests just pipeline URL
|
|
19171
19174
|
*
|
|
19175
|
+
* Note: This is a pure structural validator and does not block private/internal network
|
|
19176
|
+
* addresses. Callers that fetch the URL server-side from an untrusted network context must
|
|
19177
|
+
* additionally apply the `assertSafeUrl` SSRF guard from the Agents Server before any
|
|
19178
|
+
* outbound request.
|
|
19179
|
+
*
|
|
19172
19180
|
* @public exported from `@promptbook/utils`
|
|
19173
19181
|
*/
|
|
19174
19182
|
function isValidAgentUrl(url) {
|
|
@@ -19182,12 +19190,6 @@ function isValidAgentUrl(url) {
|
|
|
19182
19190
|
// TODO: [🐠]
|
|
19183
19191
|
return false;
|
|
19184
19192
|
}
|
|
19185
|
-
/*
|
|
19186
|
-
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
19187
|
-
if (isUrlOnPrivateNetwork(url)) {
|
|
19188
|
-
return false;
|
|
19189
|
-
}
|
|
19190
|
-
*/
|
|
19191
19193
|
return true;
|
|
19192
19194
|
}
|
|
19193
19195
|
// TODO: [🐠] Maybe more info why the URL is invalid
|
|
@@ -19363,8 +19365,11 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
19363
19365
|
if (!trimmedContent) {
|
|
19364
19366
|
return requirements;
|
|
19365
19367
|
}
|
|
19366
|
-
|
|
19367
|
-
|
|
19368
|
+
const goalSection = spaceTrim$1((block) => `
|
|
19369
|
+
## Goal
|
|
19370
|
+
|
|
19371
|
+
${block(trimmedContent)}
|
|
19372
|
+
`);
|
|
19368
19373
|
const requirementsWithGoal = this.appendToSystemMessage(requirements, goalSection, '\n\n');
|
|
19369
19374
|
return this.appendToPromptSuffix(requirementsWithGoal, trimmedContent);
|
|
19370
19375
|
}
|
|
@@ -19750,8 +19755,11 @@ class LanguageCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
19750
19755
|
if (!trimmedContent) {
|
|
19751
19756
|
return requirements;
|
|
19752
19757
|
}
|
|
19753
|
-
|
|
19754
|
-
|
|
19758
|
+
const languageSection = spaceTrim$1((block) => `
|
|
19759
|
+
## Language
|
|
19760
|
+
|
|
19761
|
+
- Your language is ${block(trimmedContent)}
|
|
19762
|
+
`);
|
|
19755
19763
|
return this.appendToSystemMessage(requirements, languageSection, '\n\n');
|
|
19756
19764
|
}
|
|
19757
19765
|
}
|
|
@@ -22094,21 +22102,22 @@ function getPointBounds(points) {
|
|
|
22094
22102
|
* @private helper of `fractalAvatarVisual`
|
|
22095
22103
|
*/
|
|
22096
22104
|
function drawDragonCurveLayer(context, points, options) {
|
|
22097
|
-
const {
|
|
22105
|
+
const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
|
|
22098
22106
|
const firstPoint = points[0];
|
|
22099
22107
|
const lastPoint = points[points.length - 1];
|
|
22100
22108
|
const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
|
|
22101
22109
|
ribbonGradient.addColorStop(0, `${primaryColor}f2`);
|
|
22102
22110
|
ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
|
|
22103
22111
|
ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
|
|
22112
|
+
// Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
|
|
22113
|
+
// context.filter blur, which triggers a costly software rasterization pass every frame.
|
|
22104
22114
|
context.save();
|
|
22105
22115
|
context.beginPath();
|
|
22106
22116
|
tracePolyline(context, points);
|
|
22107
|
-
context.strokeStyle = `${shadowColor}
|
|
22108
|
-
context.lineWidth = strokeWidth *
|
|
22117
|
+
context.strokeStyle = `${shadowColor}48`;
|
|
22118
|
+
context.lineWidth = strokeWidth * 4.5;
|
|
22109
22119
|
context.lineJoin = 'round';
|
|
22110
22120
|
context.lineCap = 'round';
|
|
22111
|
-
context.filter = `blur(${size * 0.022}px)`;
|
|
22112
22121
|
context.stroke();
|
|
22113
22122
|
context.restore();
|
|
22114
22123
|
context.beginPath();
|
|
@@ -22570,7 +22579,7 @@ function fillTextureRect(texture, x, y, width, height, color) {
|
|
|
22570
22579
|
*
|
|
22571
22580
|
* @private helper of `minecraft2AvatarVisual`
|
|
22572
22581
|
*/
|
|
22573
|
-
const LIGHT_DIRECTION$
|
|
22582
|
+
const LIGHT_DIRECTION$4 = normalizeVector3({
|
|
22574
22583
|
x: 0.4,
|
|
22575
22584
|
y: -0.65,
|
|
22576
22585
|
z: 0.92,
|
|
@@ -22692,11 +22701,23 @@ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY,
|
|
|
22692
22701
|
* @private helper of `minecraft2AvatarVisual`
|
|
22693
22702
|
*/
|
|
22694
22703
|
function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
|
|
22704
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.03;
|
|
22705
|
+
const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
|
|
22706
|
+
const rx = size * (0.16 + interaction.intensity * 0.015);
|
|
22707
|
+
const ry = size * 0.055;
|
|
22708
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
22695
22709
|
context.save();
|
|
22696
|
-
context.
|
|
22697
|
-
context.
|
|
22710
|
+
context.translate(cx, cy);
|
|
22711
|
+
context.scale(1, ry / rx);
|
|
22712
|
+
const blurRadius = rx * 1.4;
|
|
22713
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
22714
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
22715
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
22716
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
22717
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
22718
|
+
context.fillStyle = shadowGradient;
|
|
22698
22719
|
context.beginPath();
|
|
22699
|
-
context.
|
|
22720
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
22700
22721
|
context.fill();
|
|
22701
22722
|
context.restore();
|
|
22702
22723
|
}
|
|
@@ -22782,7 +22803,7 @@ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
|
|
|
22782
22803
|
corners: projectedCorners,
|
|
22783
22804
|
texture: faceDefinition.texture,
|
|
22784
22805
|
averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
|
|
22785
|
-
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$
|
|
22806
|
+
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$4), -1, 1),
|
|
22786
22807
|
outlineColor: cuboid.outlineColor,
|
|
22787
22808
|
};
|
|
22788
22809
|
});
|
|
@@ -22920,13 +22941,27 @@ const minecraftAvatarVisual = {
|
|
|
22920
22941
|
spotlight.addColorStop(1, `${palette.highlight}00`);
|
|
22921
22942
|
context.fillStyle = spotlight;
|
|
22922
22943
|
context.fillRect(0, 0, size, size);
|
|
22923
|
-
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
|
|
22929
|
-
|
|
22944
|
+
{
|
|
22945
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
22946
|
+
const cx = size * 0.5;
|
|
22947
|
+
const cy = size * 0.86;
|
|
22948
|
+
const rx = size * 0.2;
|
|
22949
|
+
const ry = size * 0.06;
|
|
22950
|
+
const blurRadius = rx * 1.4;
|
|
22951
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
22952
|
+
shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
|
|
22953
|
+
shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
|
|
22954
|
+
shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
|
|
22955
|
+
shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
|
|
22956
|
+
context.save();
|
|
22957
|
+
context.translate(cx, cy);
|
|
22958
|
+
context.scale(1, ry / rx);
|
|
22959
|
+
context.fillStyle = shadowGradient;
|
|
22960
|
+
context.beginPath();
|
|
22961
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
22962
|
+
context.fill();
|
|
22963
|
+
context.restore();
|
|
22964
|
+
}
|
|
22930
22965
|
drawVoxelCuboid(context, {
|
|
22931
22966
|
x: bodyX,
|
|
22932
22967
|
y: bodyY,
|
|
@@ -23455,7 +23490,7 @@ function resolveSeededIntegerRange(random, minimumValue, maximumValue) {
|
|
|
23455
23490
|
*
|
|
23456
23491
|
* @private helper of `octopus3AvatarVisual`
|
|
23457
23492
|
*/
|
|
23458
|
-
function formatAlphaHex$
|
|
23493
|
+
function formatAlphaHex$2(opacity) {
|
|
23459
23494
|
return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
|
|
23460
23495
|
.toString(16)
|
|
23461
23496
|
.padStart(2, '0');
|
|
@@ -23834,7 +23869,7 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
|
|
|
23834
23869
|
context.beginPath();
|
|
23835
23870
|
context.moveTo(-radiusX * 0.74, radiusY * 0.2);
|
|
23836
23871
|
context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
|
|
23837
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex$
|
|
23872
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$2(eyeStyle.lowerLidOpacity)}`;
|
|
23838
23873
|
context.lineWidth = radiusX * 0.08;
|
|
23839
23874
|
context.lineCap = 'round';
|
|
23840
23875
|
context.stroke();
|
|
@@ -23915,7 +23950,7 @@ function drawProjectedOrganicEye(context, localCenter, radiusX, radiusY, center,
|
|
|
23915
23950
|
context.beginPath();
|
|
23916
23951
|
context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
23917
23952
|
context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
23918
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
|
|
23953
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
|
|
23919
23954
|
context.lineWidth = projectedRadiusX * 0.08;
|
|
23920
23955
|
context.lineCap = 'round';
|
|
23921
23956
|
context.stroke();
|
|
@@ -23961,7 +23996,7 @@ function drawProjectedQuad(context, corners, fillStyle) {
|
|
|
23961
23996
|
*
|
|
23962
23997
|
* @private helper of the 3D octopus avatar visuals
|
|
23963
23998
|
*/
|
|
23964
|
-
function formatAlphaHex(opacity) {
|
|
23999
|
+
function formatAlphaHex$1(opacity) {
|
|
23965
24000
|
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
23966
24001
|
.toString(16)
|
|
23967
24002
|
.padStart(2, '0');
|
|
@@ -23973,11 +24008,40 @@ function formatAlphaHex(opacity) {
|
|
|
23973
24008
|
*
|
|
23974
24009
|
* @private helper of `octopus3dAvatarVisual`
|
|
23975
24010
|
*/
|
|
23976
|
-
const LIGHT_DIRECTION$
|
|
24011
|
+
const LIGHT_DIRECTION$3 = normalizeVector3({
|
|
23977
24012
|
x: 0.48,
|
|
23978
24013
|
y: -0.62,
|
|
23979
24014
|
z: 0.94,
|
|
23980
24015
|
});
|
|
24016
|
+
/**
|
|
24017
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
24018
|
+
*
|
|
24019
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
24020
|
+
*/
|
|
24021
|
+
const octopus3dStableStateCache = new WeakMap();
|
|
24022
|
+
/**
|
|
24023
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
24024
|
+
*
|
|
24025
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
24026
|
+
*/
|
|
24027
|
+
function getOctopus3dStableState(createRandom) {
|
|
24028
|
+
const cached = octopus3dStableStateCache.get(createRandom);
|
|
24029
|
+
if (cached !== undefined) {
|
|
24030
|
+
return cached;
|
|
24031
|
+
}
|
|
24032
|
+
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
24033
|
+
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
24034
|
+
const leftEyePhaseOffset = eyeRandom() * 0.6;
|
|
24035
|
+
const rightEyePhaseOffset = eyeRandom() * 0.6;
|
|
24036
|
+
const state = {
|
|
24037
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
24038
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
24039
|
+
leftEyePhaseOffset,
|
|
24040
|
+
rightEyePhaseOffset,
|
|
24041
|
+
};
|
|
24042
|
+
octopus3dStableStateCache.set(createRandom, state);
|
|
24043
|
+
return state;
|
|
24044
|
+
}
|
|
23981
24045
|
/**
|
|
23982
24046
|
* Proper 3D Octopus visual built from projected organic meshes and tentacles.
|
|
23983
24047
|
*
|
|
@@ -23990,10 +24054,7 @@ const octopus3dAvatarVisual = {
|
|
|
23990
24054
|
isAnimated: true,
|
|
23991
24055
|
supportsPointerTracking: true,
|
|
23992
24056
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
23993
|
-
const morphologyProfile =
|
|
23994
|
-
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
23995
|
-
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
23996
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
24057
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
|
|
23997
24058
|
const sceneCenterX = size * 0.5;
|
|
23998
24059
|
const sceneCenterY = size * 0.56;
|
|
23999
24060
|
const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
|
|
@@ -24090,12 +24151,12 @@ const octopus3dAvatarVisual = {
|
|
|
24090
24151
|
x: -faceEyeSpacing,
|
|
24091
24152
|
y: faceEyeYOffset,
|
|
24092
24153
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
|
|
24093
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
24154
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
24094
24155
|
drawProjectedOrganicEye(context, {
|
|
24095
24156
|
x: faceEyeSpacing,
|
|
24096
24157
|
y: faceEyeYOffset,
|
|
24097
24158
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
|
|
24098
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 +
|
|
24159
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
24099
24160
|
drawProjectedOrganicMouth(context, [
|
|
24100
24161
|
{
|
|
24101
24162
|
x: -mouthHalfWidth,
|
|
@@ -24139,14 +24200,28 @@ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCent
|
|
|
24139
24200
|
/**
|
|
24140
24201
|
* Draws the soft ground shadow below the octopus.
|
|
24141
24202
|
*
|
|
24203
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
24204
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
24205
|
+
*
|
|
24142
24206
|
* @private helper of `octopus3dAvatarVisual`
|
|
24143
24207
|
*/
|
|
24144
24208
|
function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
|
|
24209
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.04;
|
|
24210
|
+
const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
|
|
24211
|
+
const rx = size * (0.18 + interaction.intensity * 0.02);
|
|
24212
|
+
const ry = size * 0.06;
|
|
24145
24213
|
context.save();
|
|
24146
|
-
context.
|
|
24147
|
-
context.
|
|
24214
|
+
context.translate(cx, cy);
|
|
24215
|
+
context.scale(1, ry / rx);
|
|
24216
|
+
const blurRadius = rx * 1.4;
|
|
24217
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
24218
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
24219
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
24220
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
24221
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
24222
|
+
context.fillStyle = shadowGradient;
|
|
24148
24223
|
context.beginPath();
|
|
24149
|
-
context.
|
|
24224
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
24150
24225
|
context.fill();
|
|
24151
24226
|
context.restore();
|
|
24152
24227
|
}
|
|
@@ -24183,7 +24258,7 @@ function resolveVisibleEllipsoidPatches(options) {
|
|
|
24183
24258
|
corners: projectedCorners,
|
|
24184
24259
|
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
24185
24260
|
transformedCorners.length,
|
|
24186
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$
|
|
24261
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
|
|
24187
24262
|
fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
|
|
24188
24263
|
outlineColor,
|
|
24189
24264
|
});
|
|
@@ -24372,11 +24447,40 @@ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
|
|
|
24372
24447
|
*
|
|
24373
24448
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24374
24449
|
*/
|
|
24375
|
-
const LIGHT_DIRECTION = normalizeVector3({
|
|
24450
|
+
const LIGHT_DIRECTION$2 = normalizeVector3({
|
|
24376
24451
|
x: 0.38,
|
|
24377
24452
|
y: -0.6,
|
|
24378
24453
|
z: 0.98,
|
|
24379
24454
|
});
|
|
24455
|
+
/**
|
|
24456
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
24457
|
+
*
|
|
24458
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
24459
|
+
*/
|
|
24460
|
+
const octopus3d2StableStateCache = new WeakMap();
|
|
24461
|
+
/**
|
|
24462
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
24463
|
+
*
|
|
24464
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
24465
|
+
*/
|
|
24466
|
+
function getOctopus3d2StableState(createRandom) {
|
|
24467
|
+
const cached = octopus3d2StableStateCache.get(createRandom);
|
|
24468
|
+
if (cached !== undefined) {
|
|
24469
|
+
return cached;
|
|
24470
|
+
}
|
|
24471
|
+
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
24472
|
+
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
24473
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
24474
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
24475
|
+
const state = {
|
|
24476
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
24477
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
24478
|
+
leftEyePhaseOffset,
|
|
24479
|
+
rightEyePhaseOffset,
|
|
24480
|
+
};
|
|
24481
|
+
octopus3d2StableStateCache.set(createRandom, state);
|
|
24482
|
+
return state;
|
|
24483
|
+
}
|
|
24380
24484
|
/**
|
|
24381
24485
|
* Octopus 3D 2 avatar visual.
|
|
24382
24486
|
*
|
|
@@ -24389,10 +24493,7 @@ const octopus3d2AvatarVisual = {
|
|
|
24389
24493
|
isAnimated: true,
|
|
24390
24494
|
supportsPointerTracking: true,
|
|
24391
24495
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
24392
|
-
const morphologyProfile =
|
|
24393
|
-
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
24394
|
-
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
24395
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
24496
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
|
|
24396
24497
|
const sceneCenterX = size * 0.5;
|
|
24397
24498
|
const sceneCenterY = size * 0.575;
|
|
24398
24499
|
const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
|
|
@@ -24445,8 +24546,8 @@ const octopus3d2AvatarVisual = {
|
|
|
24445
24546
|
const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
|
|
24446
24547
|
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
24447
24548
|
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
24448
|
-
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
24449
|
-
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 +
|
|
24549
|
+
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
24550
|
+
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
24450
24551
|
drawProjectedOrganicMouth(context, [
|
|
24451
24552
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
24452
24553
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
@@ -24475,54 +24576,118 @@ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, scene
|
|
|
24475
24576
|
/**
|
|
24476
24577
|
* Draws the soft floor shadow that anchors the single mesh in the frame.
|
|
24477
24578
|
*
|
|
24579
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
24580
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
24581
|
+
*
|
|
24478
24582
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24479
24583
|
*/
|
|
24480
24584
|
function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
24585
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
24586
|
+
const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
|
|
24587
|
+
const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
|
|
24588
|
+
const ry = size * 0.062;
|
|
24481
24589
|
context.save();
|
|
24482
|
-
context.
|
|
24483
|
-
context.
|
|
24590
|
+
context.translate(cx, cy);
|
|
24591
|
+
context.scale(1, ry / rx);
|
|
24592
|
+
const blurRadius = rx * 1.4;
|
|
24593
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
24594
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
24595
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
24596
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
24597
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
24598
|
+
context.fillStyle = shadowGradient;
|
|
24484
24599
|
context.beginPath();
|
|
24485
|
-
context.
|
|
24600
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
24486
24601
|
context.fill();
|
|
24487
24602
|
context.restore();
|
|
24488
24603
|
}
|
|
24604
|
+
/**
|
|
24605
|
+
* Number of latitude segments used by the single blobby octopus mesh.
|
|
24606
|
+
*
|
|
24607
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
24608
|
+
*/
|
|
24609
|
+
const LATITUDE_PATCH_COUNT$2 = 12;
|
|
24610
|
+
/**
|
|
24611
|
+
* Number of longitude segments used by the single blobby octopus mesh.
|
|
24612
|
+
*
|
|
24613
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
24614
|
+
*/
|
|
24615
|
+
const LONGITUDE_PATCH_COUNT$2 = 24;
|
|
24489
24616
|
/**
|
|
24490
24617
|
* Resolves all visible projected patches for the single blobby octopus mesh.
|
|
24491
24618
|
*
|
|
24619
|
+
* Within a single frame, mesh corner samples and longitude-only lobe-wave values are
|
|
24620
|
+
* quantized to the patch grid and computed once each rather than re-evaluated for every
|
|
24621
|
+
* patch corner — the patch loop alone would call `sampleBlobbyOctopusSurfacePoint`
|
|
24622
|
+
* `latitudePatchCount * longitudePatchCount * 4` times without caching, even though most
|
|
24623
|
+
* corners are shared between neighboring patches.
|
|
24624
|
+
*
|
|
24492
24625
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24493
24626
|
*/
|
|
24494
24627
|
function resolveVisibleBlobbyOctopusPatches(options) {
|
|
24495
24628
|
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
|
|
24496
|
-
const latitudePatchCount =
|
|
24497
|
-
const longitudePatchCount =
|
|
24629
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT$2;
|
|
24630
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT$2;
|
|
24498
24631
|
const surfacePatches = [];
|
|
24632
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
24633
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
24634
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
24635
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
24636
|
+
}
|
|
24637
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
24638
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
24639
|
+
}
|
|
24640
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
24641
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
24642
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveLowerLobeWave(longitudeBoundaries[boundaryIndex], morphologyProfile, animationPhase, timeMs);
|
|
24643
|
+
}
|
|
24644
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
24645
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
24646
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
24647
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
24648
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
24649
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
24650
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
24651
|
+
const cornerSample = sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
24652
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
24653
|
+
}
|
|
24654
|
+
}
|
|
24499
24655
|
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
24500
|
-
const startLatitude =
|
|
24501
|
-
const endLatitude =
|
|
24656
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
24657
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
24502
24658
|
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
24503
24659
|
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
24660
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
24661
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
24504
24662
|
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
24505
|
-
const startLongitude =
|
|
24506
|
-
const endLongitude =
|
|
24663
|
+
const startLongitude = longitudeBoundaries[longitudeIndex];
|
|
24664
|
+
const endLongitude = longitudeBoundaries[longitudeIndex + 1];
|
|
24507
24665
|
const centerLongitude = (startLongitude + endLongitude) / 2;
|
|
24508
|
-
const
|
|
24509
|
-
|
|
24510
|
-
|
|
24511
|
-
|
|
24512
|
-
|
|
24666
|
+
const transformedCorners = [
|
|
24667
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
24668
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
24669
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
24670
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
24513
24671
|
];
|
|
24514
|
-
const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
|
|
24515
24672
|
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
24516
24673
|
if (surfaceNormal.z <= 0.01) {
|
|
24517
24674
|
continue;
|
|
24518
24675
|
}
|
|
24519
|
-
const projectedCorners =
|
|
24676
|
+
const projectedCorners = [
|
|
24677
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
24678
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
24679
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
24680
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
24681
|
+
];
|
|
24520
24682
|
surfacePatches.push({
|
|
24521
24683
|
corners: projectedCorners,
|
|
24522
|
-
averageDepth: transformedCorners.
|
|
24523
|
-
transformedCorners.
|
|
24524
|
-
|
|
24525
|
-
|
|
24684
|
+
averageDepth: (transformedCorners[0].z +
|
|
24685
|
+
transformedCorners[1].z +
|
|
24686
|
+
transformedCorners[2].z +
|
|
24687
|
+
transformedCorners[3].z) /
|
|
24688
|
+
4,
|
|
24689
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
|
|
24690
|
+
fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
|
|
24526
24691
|
outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
|
|
24527
24692
|
});
|
|
24528
24693
|
}
|
|
@@ -24538,12 +24703,21 @@ function resolveVisibleBlobbyOctopusPatches(options) {
|
|
|
24538
24703
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24539
24704
|
*/
|
|
24540
24705
|
function sampleBlobbyOctopusSurfacePoint(options, latitude, longitude) {
|
|
24706
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
24707
|
+
return sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs));
|
|
24708
|
+
}
|
|
24709
|
+
/**
|
|
24710
|
+
* Samples one point on the continuous Octopus 3D 2 surface using a precomputed lower-lobe wave
|
|
24711
|
+
* to skip the per-call trig evaluation for `latitudePatchCount + 1` longitude-shared corners.
|
|
24712
|
+
*
|
|
24713
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
24714
|
+
*/
|
|
24715
|
+
function sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, lowerLobeWave) {
|
|
24541
24716
|
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
24542
24717
|
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
24543
24718
|
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
24544
24719
|
const upperBlend = Math.pow(1 - verticalProgress, 1.2);
|
|
24545
24720
|
const lowerBlend = Math.pow(verticalProgress, 1.42);
|
|
24546
|
-
const lowerLobeWave = resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs);
|
|
24547
24721
|
const skirtEnvelope = Math.pow(cosineLatitude, 0.5) * lowerBlend;
|
|
24548
24722
|
const horizontalScale = 1.02 +
|
|
24549
24723
|
skirtEnvelope * (0.34 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.22 + lowerLobeWave * 0.22) -
|
|
@@ -24581,7 +24755,7 @@ function resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, time
|
|
|
24581
24755
|
*
|
|
24582
24756
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24583
24757
|
*/
|
|
24584
|
-
function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, lowerLobeWave) {
|
|
24758
|
+
function resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, forwardness, lowerLobeWave) {
|
|
24585
24759
|
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.12 - forwardness * 0.07, 0, 1);
|
|
24586
24760
|
if (tonalProgress < 0.16) {
|
|
24587
24761
|
return palette.highlight;
|
|
@@ -24621,6 +24795,1384 @@ function drawBlobbySurfacePatch(context, surfacePatch) {
|
|
|
24621
24795
|
context.restore();
|
|
24622
24796
|
}
|
|
24623
24797
|
|
|
24798
|
+
/* eslint-disable no-magic-numbers */
|
|
24799
|
+
/**
|
|
24800
|
+
* Light direction used by the continuous octopus mesh shading.
|
|
24801
|
+
*
|
|
24802
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
24803
|
+
*/
|
|
24804
|
+
const LIGHT_DIRECTION$1 = normalizeVector3({
|
|
24805
|
+
x: 0.34,
|
|
24806
|
+
y: -0.62,
|
|
24807
|
+
z: 1,
|
|
24808
|
+
});
|
|
24809
|
+
/**
|
|
24810
|
+
* Real-octopus tentacle count used by the continuous lower mesh.
|
|
24811
|
+
*
|
|
24812
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
24813
|
+
*/
|
|
24814
|
+
const OCTOPUS_TENTACLE_COUNT$1 = 8;
|
|
24815
|
+
/**
|
|
24816
|
+
* Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
|
|
24817
|
+
* mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
|
|
24818
|
+
* React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
|
|
24819
|
+
*
|
|
24820
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
24821
|
+
*/
|
|
24822
|
+
const stableStateCache = new WeakMap();
|
|
24823
|
+
/**
|
|
24824
|
+
* Returns the stable per-avatar state, computing it on first access and returning the cached
|
|
24825
|
+
* result on every subsequent call within the same `<Avatar/>` mount.
|
|
24826
|
+
*
|
|
24827
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
24828
|
+
*/
|
|
24829
|
+
function getOctopus3d3StableState(createRandom) {
|
|
24830
|
+
const cached = stableStateCache.get(createRandom);
|
|
24831
|
+
if (cached !== undefined) {
|
|
24832
|
+
return cached;
|
|
24833
|
+
}
|
|
24834
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
24835
|
+
const animationRandom = createRandom('octopus3d3-animation-profile');
|
|
24836
|
+
const eyeRandom = createRandom('octopus3d3-eye-profile');
|
|
24837
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
24838
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
24839
|
+
const state = {
|
|
24840
|
+
morphologyProfile,
|
|
24841
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
24842
|
+
leftEyePhaseOffset,
|
|
24843
|
+
rightEyePhaseOffset,
|
|
24844
|
+
tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
24845
|
+
};
|
|
24846
|
+
stableStateCache.set(createRandom, state);
|
|
24847
|
+
return state;
|
|
24848
|
+
}
|
|
24849
|
+
/**
|
|
24850
|
+
* Octopus 3D 3 avatar visual.
|
|
24851
|
+
*
|
|
24852
|
+
* @private built-in avatar visual
|
|
24853
|
+
*/
|
|
24854
|
+
const octopus3d3AvatarVisual = {
|
|
24855
|
+
id: 'octopus3d3',
|
|
24856
|
+
title: 'Octopus 3D 3',
|
|
24857
|
+
description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
|
|
24858
|
+
isAnimated: true,
|
|
24859
|
+
supportsPointerTracking: true,
|
|
24860
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
24861
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
|
|
24862
|
+
const sceneCenterX = size * 0.5;
|
|
24863
|
+
const sceneCenterY = size * 0.535;
|
|
24864
|
+
const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
|
|
24865
|
+
const meshCenter = {
|
|
24866
|
+
x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
|
|
24867
|
+
y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
|
|
24868
|
+
z: interaction.intensity * size * 0.018,
|
|
24869
|
+
};
|
|
24870
|
+
const rotationY = -0.1 +
|
|
24871
|
+
Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
|
|
24872
|
+
interaction.bodyOffsetX * 0.22 +
|
|
24873
|
+
interaction.gazeX * 0.88;
|
|
24874
|
+
const rotationX = -0.07 +
|
|
24875
|
+
Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
|
|
24876
|
+
interaction.bodyOffsetY * 0.08 -
|
|
24877
|
+
interaction.gazeY * 0.38;
|
|
24878
|
+
const surfaceOptions = {
|
|
24879
|
+
radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
|
|
24880
|
+
radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
|
|
24881
|
+
radiusZ: size *
|
|
24882
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
24883
|
+
(1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
|
|
24884
|
+
morphologyProfile,
|
|
24885
|
+
timeMs,
|
|
24886
|
+
animationPhase,
|
|
24887
|
+
tentacleProfiles,
|
|
24888
|
+
};
|
|
24889
|
+
const surfacePatches = resolveVisibleContinuousOctopusPatches({
|
|
24890
|
+
...surfaceOptions,
|
|
24891
|
+
center: meshCenter,
|
|
24892
|
+
rotationX,
|
|
24893
|
+
rotationY,
|
|
24894
|
+
sceneCenterX,
|
|
24895
|
+
sceneCenterY,
|
|
24896
|
+
size,
|
|
24897
|
+
palette,
|
|
24898
|
+
});
|
|
24899
|
+
const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
|
|
24900
|
+
const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
|
|
24901
|
+
const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
|
|
24902
|
+
const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
|
|
24903
|
+
const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
|
|
24904
|
+
const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
|
|
24905
|
+
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
|
|
24906
|
+
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
|
|
24907
|
+
drawAvatarFrame(context, size, palette);
|
|
24908
|
+
drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
24909
|
+
drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
|
|
24910
|
+
for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
24911
|
+
drawContinuousSurfacePatch(context, surfacePatch);
|
|
24912
|
+
}
|
|
24913
|
+
drawProjectedSurfaceCurrents({
|
|
24914
|
+
context,
|
|
24915
|
+
surfaceOptions,
|
|
24916
|
+
center: meshCenter,
|
|
24917
|
+
rotationX,
|
|
24918
|
+
rotationY,
|
|
24919
|
+
sceneCenterX,
|
|
24920
|
+
sceneCenterY,
|
|
24921
|
+
size,
|
|
24922
|
+
palette,
|
|
24923
|
+
morphologyProfile,
|
|
24924
|
+
timeMs,
|
|
24925
|
+
animationPhase,
|
|
24926
|
+
});
|
|
24927
|
+
drawProjectedTentacleSuckers({
|
|
24928
|
+
context,
|
|
24929
|
+
surfaceOptions,
|
|
24930
|
+
center: meshCenter,
|
|
24931
|
+
rotationX,
|
|
24932
|
+
rotationY,
|
|
24933
|
+
sceneCenterX,
|
|
24934
|
+
sceneCenterY,
|
|
24935
|
+
size,
|
|
24936
|
+
palette,
|
|
24937
|
+
});
|
|
24938
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
24939
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
24940
|
+
drawProjectedOrganicMouth(context, [
|
|
24941
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
24942
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
24943
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
|
|
24944
|
+
], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
|
|
24945
|
+
},
|
|
24946
|
+
};
|
|
24947
|
+
/**
|
|
24948
|
+
* Creates seeded tentacle-lobe profiles around the visible lower octopus body.
|
|
24949
|
+
*
|
|
24950
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
24951
|
+
*/
|
|
24952
|
+
function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
24953
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT$1 }, (_, tentacleIndex) => {
|
|
24954
|
+
const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
|
|
24955
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT$1 - 1);
|
|
24956
|
+
return {
|
|
24957
|
+
centerLongitude: -Math.PI * 0.86 +
|
|
24958
|
+
progress * Math.PI * 1.72 +
|
|
24959
|
+
(tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
|
|
24960
|
+
widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
|
|
24961
|
+
lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
|
|
24962
|
+
swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
|
|
24963
|
+
depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
|
|
24964
|
+
phase: tentacleRandom() * Math.PI * 2,
|
|
24965
|
+
suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
|
|
24966
|
+
};
|
|
24967
|
+
});
|
|
24968
|
+
}
|
|
24969
|
+
/**
|
|
24970
|
+
* Draws the soft underwater atmosphere behind the continuous octopus mesh.
|
|
24971
|
+
*
|
|
24972
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
24973
|
+
*/
|
|
24974
|
+
function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
24975
|
+
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));
|
|
24976
|
+
glowGradient.addColorStop(0, `${palette.highlight}66`);
|
|
24977
|
+
glowGradient.addColorStop(0.34, `${palette.accent}2e`);
|
|
24978
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
24979
|
+
context.fillStyle = glowGradient;
|
|
24980
|
+
context.fillRect(0, 0, size, size);
|
|
24981
|
+
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);
|
|
24982
|
+
lowerGradient.addColorStop(0, `${palette.secondary}25`);
|
|
24983
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
24984
|
+
context.fillStyle = lowerGradient;
|
|
24985
|
+
context.fillRect(0, 0, size, size);
|
|
24986
|
+
}
|
|
24987
|
+
/**
|
|
24988
|
+
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
24989
|
+
*
|
|
24990
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
24991
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
24992
|
+
*
|
|
24993
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
24994
|
+
*/
|
|
24995
|
+
function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
24996
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
24997
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
24998
|
+
const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
|
|
24999
|
+
const ry = size * 0.06;
|
|
25000
|
+
// Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
|
|
25001
|
+
// then fill with a radial gradient that approximates the blurry edge without context.filter.
|
|
25002
|
+
context.save();
|
|
25003
|
+
context.translate(cx, cy);
|
|
25004
|
+
context.scale(1, ry / rx);
|
|
25005
|
+
const blurRadius = rx * 1.4;
|
|
25006
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
25007
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
25008
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
25009
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
25010
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
25011
|
+
context.fillStyle = shadowGradient;
|
|
25012
|
+
context.beginPath();
|
|
25013
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
25014
|
+
context.fill();
|
|
25015
|
+
context.restore();
|
|
25016
|
+
}
|
|
25017
|
+
/**
|
|
25018
|
+
* Number of latitude segments used by the continuous Octopus 3D 3 mesh.
|
|
25019
|
+
*
|
|
25020
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25021
|
+
*/
|
|
25022
|
+
const LATITUDE_PATCH_COUNT$1 = 16;
|
|
25023
|
+
/**
|
|
25024
|
+
* Number of longitude segments used by the continuous Octopus 3D 3 mesh.
|
|
25025
|
+
*
|
|
25026
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25027
|
+
*/
|
|
25028
|
+
const LONGITUDE_PATCH_COUNT$1 = 40;
|
|
25029
|
+
/**
|
|
25030
|
+
* Resolves visible projected patches for the continuous octopus mesh.
|
|
25031
|
+
*
|
|
25032
|
+
* Within a single frame, mesh corner samples and longitude-only computations (tentacle
|
|
25033
|
+
* influence and lobe wave) are quantized to the patch grid and computed once each rather
|
|
25034
|
+
* than re-evaluated for every patch corner — the patch loop alone calls `sampleContinuousOctopusSurfacePoint`
|
|
25035
|
+
* for `latitudePatchCount * longitudePatchCount * 4` corners without caching, and each call
|
|
25036
|
+
* triggers an inner 8-iteration `Math.exp` loop, which dominates the per-frame cost.
|
|
25037
|
+
*
|
|
25038
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25039
|
+
*/
|
|
25040
|
+
function resolveVisibleContinuousOctopusPatches(options) {
|
|
25041
|
+
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
25042
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT$1;
|
|
25043
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT$1;
|
|
25044
|
+
const surfacePatches = [];
|
|
25045
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
25046
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
25047
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
25048
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
25049
|
+
}
|
|
25050
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
25051
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
25052
|
+
}
|
|
25053
|
+
const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
|
|
25054
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
25055
|
+
const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
|
|
25056
|
+
const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
25057
|
+
const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
25058
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
25059
|
+
const cornerLongitude = longitudeBoundaries[boundaryIndex];
|
|
25060
|
+
cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveContinuousTentacleInfluence(options, cornerLongitude);
|
|
25061
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveContinuousLobeWave(options, cornerLongitude);
|
|
25062
|
+
}
|
|
25063
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
25064
|
+
const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
|
|
25065
|
+
cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveContinuousTentacleInfluence(options, patchCenterLongitude);
|
|
25066
|
+
cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveContinuousLobeWave(options, patchCenterLongitude);
|
|
25067
|
+
cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
|
|
25068
|
+
}
|
|
25069
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
25070
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
25071
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
25072
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
25073
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
25074
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
25075
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
25076
|
+
const cornerSample = sampleContinuousOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
25077
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
25078
|
+
}
|
|
25079
|
+
}
|
|
25080
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
25081
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
25082
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
25083
|
+
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
25084
|
+
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
25085
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
25086
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
25087
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
25088
|
+
const transformedCorners = [
|
|
25089
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
25090
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
25091
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
25092
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
25093
|
+
];
|
|
25094
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
25095
|
+
if (surfaceNormal.z <= 0.008) {
|
|
25096
|
+
continue;
|
|
25097
|
+
}
|
|
25098
|
+
const projectedCorners = [
|
|
25099
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
25100
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
25101
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
25102
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
25103
|
+
];
|
|
25104
|
+
surfacePatches.push({
|
|
25105
|
+
corners: projectedCorners,
|
|
25106
|
+
averageDepth: (transformedCorners[0].z +
|
|
25107
|
+
transformedCorners[1].z +
|
|
25108
|
+
transformedCorners[2].z +
|
|
25109
|
+
transformedCorners[3].z) /
|
|
25110
|
+
4,
|
|
25111
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
|
|
25112
|
+
fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
25113
|
+
outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
|
|
25114
|
+
});
|
|
25115
|
+
}
|
|
25116
|
+
}
|
|
25117
|
+
return surfacePatches;
|
|
25118
|
+
}
|
|
25119
|
+
/**
|
|
25120
|
+
* Samples one point on the continuous Octopus 3D 3 surface.
|
|
25121
|
+
*
|
|
25122
|
+
* The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
|
|
25123
|
+
* tentacled while still being rendered as one connected blobby mesh.
|
|
25124
|
+
*
|
|
25125
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25126
|
+
*/
|
|
25127
|
+
function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
|
|
25128
|
+
return sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveContinuousTentacleInfluence(options, longitude), resolveContinuousLobeWave(options, longitude));
|
|
25129
|
+
}
|
|
25130
|
+
/**
|
|
25131
|
+
* Samples one point on the continuous Octopus 3D 3 surface using precomputed longitude-only
|
|
25132
|
+
* values to skip the per-call `Math.exp` tentacle-influence loop and the lobe-wave trig call.
|
|
25133
|
+
*
|
|
25134
|
+
* The patch loop quantizes the mesh into a fixed `(latitudePatchCount + 1) * (longitudePatchCount + 1)`
|
|
25135
|
+
* corner grid, so the same longitude is reused across every latitude row and each
|
|
25136
|
+
* tentacle/lobe value can be computed once per frame instead of `latitudePatchCount * 4` times.
|
|
25137
|
+
*
|
|
25138
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25139
|
+
*/
|
|
25140
|
+
function sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
|
|
25141
|
+
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
25142
|
+
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
25143
|
+
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
25144
|
+
const upperBlend = Math.pow(1 - verticalProgress, 1.28);
|
|
25145
|
+
const lowerBlend = smoothStep$1(0.38, 1, verticalProgress);
|
|
25146
|
+
const tipBlend = smoothStep$1(0.68, 1, verticalProgress);
|
|
25147
|
+
const centerPull = resolveSignedAngularDistance$1(longitude, tentacleInfluence.centerLongitude);
|
|
25148
|
+
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
|
|
25149
|
+
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
25150
|
+
animationPhase * 0.6 +
|
|
25151
|
+
timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
|
|
25152
|
+
(0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
|
|
25153
|
+
(0.3 + lowerBlend * 0.7);
|
|
25154
|
+
const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
|
|
25155
|
+
lowerBlend *
|
|
25156
|
+
tentacleInfluence.core *
|
|
25157
|
+
tentacleInfluence.swayScale;
|
|
25158
|
+
const horizontalScale = 1.04 +
|
|
25159
|
+
mantleRipple +
|
|
25160
|
+
lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
|
|
25161
|
+
lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
|
|
25162
|
+
upperBlend * 0.08;
|
|
25163
|
+
const depthScale = 1.06 +
|
|
25164
|
+
upperBlend * 0.16 +
|
|
25165
|
+
Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
|
|
25166
|
+
lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
|
|
25167
|
+
Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
|
|
25168
|
+
const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) * radiusX;
|
|
25169
|
+
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
25170
|
+
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
|
|
25171
|
+
const lowerDrop = lowerBlend *
|
|
25172
|
+
radiusY *
|
|
25173
|
+
(0.18 +
|
|
25174
|
+
tentacleInfluence.core *
|
|
25175
|
+
(0.38 +
|
|
25176
|
+
tentacleInfluence.lengthScale * 0.22 +
|
|
25177
|
+
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
|
|
25178
|
+
return {
|
|
25179
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX + tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
|
|
25180
|
+
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
|
|
25181
|
+
upperBlend * radiusY * 0.1 +
|
|
25182
|
+
lowerDrop +
|
|
25183
|
+
Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
|
|
25184
|
+
Math.cos(timeMs / 880 + tentacleInfluence.phase) *
|
|
25185
|
+
lowerBlend *
|
|
25186
|
+
tipBlend *
|
|
25187
|
+
tentacleInfluence.core *
|
|
25188
|
+
radiusY *
|
|
25189
|
+
0.034,
|
|
25190
|
+
z: Math.cos(effectiveLongitude) * planarRadiusZ +
|
|
25191
|
+
Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
|
|
25192
|
+
lowerBlend *
|
|
25193
|
+
tentacleInfluence.core *
|
|
25194
|
+
radiusZ *
|
|
25195
|
+
0.04,
|
|
25196
|
+
};
|
|
25197
|
+
}
|
|
25198
|
+
/**
|
|
25199
|
+
* Blends nearby seeded tentacle profiles at one mesh longitude.
|
|
25200
|
+
*
|
|
25201
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25202
|
+
*/
|
|
25203
|
+
function resolveContinuousTentacleInfluence(options, longitude) {
|
|
25204
|
+
let totalWeight = 0;
|
|
25205
|
+
let weightedSin = 0;
|
|
25206
|
+
let weightedCos = 0;
|
|
25207
|
+
let weightedWidthScale = 0;
|
|
25208
|
+
let weightedLengthScale = 0;
|
|
25209
|
+
let weightedSwayScale = 0;
|
|
25210
|
+
let weightedDepthScale = 0;
|
|
25211
|
+
let weightedPhase = 0;
|
|
25212
|
+
for (const tentacleProfile of options.tentacleProfiles) {
|
|
25213
|
+
const distance = Math.abs(resolveSignedAngularDistance$1(longitude, tentacleProfile.centerLongitude));
|
|
25214
|
+
const width = 0.2 * tentacleProfile.widthScale;
|
|
25215
|
+
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
25216
|
+
totalWeight += weight;
|
|
25217
|
+
weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
|
|
25218
|
+
weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
|
|
25219
|
+
weightedWidthScale += tentacleProfile.widthScale * weight;
|
|
25220
|
+
weightedLengthScale += tentacleProfile.lengthScale * weight;
|
|
25221
|
+
weightedSwayScale += tentacleProfile.swayScale * weight;
|
|
25222
|
+
weightedDepthScale += tentacleProfile.depthScale * weight;
|
|
25223
|
+
weightedPhase += tentacleProfile.phase * weight;
|
|
25224
|
+
}
|
|
25225
|
+
if (totalWeight < 0.0001) {
|
|
25226
|
+
return {
|
|
25227
|
+
core: 0,
|
|
25228
|
+
centerLongitude: longitude,
|
|
25229
|
+
widthScale: 1,
|
|
25230
|
+
lengthScale: 1,
|
|
25231
|
+
swayScale: 1,
|
|
25232
|
+
depthScale: 1,
|
|
25233
|
+
phase: 0,
|
|
25234
|
+
};
|
|
25235
|
+
}
|
|
25236
|
+
return {
|
|
25237
|
+
core: clampNumber$1(totalWeight, 0, 1),
|
|
25238
|
+
centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
|
|
25239
|
+
widthScale: weightedWidthScale / totalWeight,
|
|
25240
|
+
lengthScale: weightedLengthScale / totalWeight,
|
|
25241
|
+
swayScale: weightedSwayScale / totalWeight,
|
|
25242
|
+
depthScale: weightedDepthScale / totalWeight,
|
|
25243
|
+
phase: weightedPhase / totalWeight,
|
|
25244
|
+
};
|
|
25245
|
+
}
|
|
25246
|
+
/**
|
|
25247
|
+
* Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
|
|
25248
|
+
*
|
|
25249
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25250
|
+
*/
|
|
25251
|
+
function resolveContinuousLobeWave(options, longitude) {
|
|
25252
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
25253
|
+
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT$1 +
|
|
25254
|
+
animationPhase +
|
|
25255
|
+
timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
|
|
25256
|
+
1) /
|
|
25257
|
+
2);
|
|
25258
|
+
}
|
|
25259
|
+
/**
|
|
25260
|
+
* Resolves one base fill tone for a patch on the continuous octopus mesh.
|
|
25261
|
+
*
|
|
25262
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25263
|
+
*/
|
|
25264
|
+
function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
|
|
25265
|
+
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
|
|
25266
|
+
if (tonalProgress < 0.14) {
|
|
25267
|
+
return palette.highlight;
|
|
25268
|
+
}
|
|
25269
|
+
if (tonalProgress < 0.32) {
|
|
25270
|
+
return palette.secondary;
|
|
25271
|
+
}
|
|
25272
|
+
if (tonalProgress < 0.72) {
|
|
25273
|
+
return forwardness > 0.55 ? palette.secondary : palette.primary;
|
|
25274
|
+
}
|
|
25275
|
+
return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
|
|
25276
|
+
}
|
|
25277
|
+
/**
|
|
25278
|
+
* Draws one projected mesh patch with soft shading and a subtle edge.
|
|
25279
|
+
*
|
|
25280
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25281
|
+
*/
|
|
25282
|
+
function drawContinuousSurfacePatch(context, surfacePatch) {
|
|
25283
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
25284
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
25285
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
|
|
25286
|
+
}
|
|
25287
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
25288
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
25289
|
+
}
|
|
25290
|
+
context.save();
|
|
25291
|
+
context.beginPath();
|
|
25292
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
25293
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
25294
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
25295
|
+
}
|
|
25296
|
+
context.closePath();
|
|
25297
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
25298
|
+
context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
|
|
25299
|
+
context.lineJoin = 'round';
|
|
25300
|
+
context.stroke();
|
|
25301
|
+
context.restore();
|
|
25302
|
+
}
|
|
25303
|
+
/**
|
|
25304
|
+
* Draws projected mantle-current lines on the front of the mesh.
|
|
25305
|
+
*
|
|
25306
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25307
|
+
*/
|
|
25308
|
+
function drawProjectedSurfaceCurrents(options) {
|
|
25309
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
|
|
25310
|
+
const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
|
|
25311
|
+
const centerIndex = (currentCount - 1) / 2;
|
|
25312
|
+
context.save();
|
|
25313
|
+
context.lineCap = 'round';
|
|
25314
|
+
context.lineJoin = 'round';
|
|
25315
|
+
for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
|
|
25316
|
+
const baseLongitude = (currentIndex - centerIndex) * 0.15;
|
|
25317
|
+
const projectedPoints = [];
|
|
25318
|
+
for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
|
|
25319
|
+
const progress = sampleIndex / 7;
|
|
25320
|
+
const latitude = -0.46 + progress * 0.74;
|
|
25321
|
+
const longitude = baseLongitude + Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
|
|
25322
|
+
const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
|
|
25323
|
+
if (scenePoint.z > center.z - size * 0.016) {
|
|
25324
|
+
projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
25325
|
+
}
|
|
25326
|
+
}
|
|
25327
|
+
if (projectedPoints.length < 3) {
|
|
25328
|
+
continue;
|
|
25329
|
+
}
|
|
25330
|
+
context.beginPath();
|
|
25331
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
25332
|
+
for (const projectedPoint of projectedPoints.slice(1)) {
|
|
25333
|
+
context.lineTo(projectedPoint.x, projectedPoint.y);
|
|
25334
|
+
}
|
|
25335
|
+
context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
|
|
25336
|
+
context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
|
|
25337
|
+
context.stroke();
|
|
25338
|
+
}
|
|
25339
|
+
context.restore();
|
|
25340
|
+
}
|
|
25341
|
+
/**
|
|
25342
|
+
* Draws small projected sucker highlights on the waving lower mesh lobes.
|
|
25343
|
+
*
|
|
25344
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25345
|
+
*/
|
|
25346
|
+
function drawProjectedTentacleSuckers(options) {
|
|
25347
|
+
const { surfaceOptions, size } = options;
|
|
25348
|
+
const { timeMs } = surfaceOptions;
|
|
25349
|
+
for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
|
|
25350
|
+
if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
|
|
25351
|
+
continue;
|
|
25352
|
+
}
|
|
25353
|
+
for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
|
|
25354
|
+
const latitude = 0.52 + suckerIndex * 0.14;
|
|
25355
|
+
const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
|
|
25356
|
+
const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
|
|
25357
|
+
drawProjectedSurfaceSpot({
|
|
25358
|
+
...options,
|
|
25359
|
+
latitude,
|
|
25360
|
+
longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
|
|
25361
|
+
radiusScale: size * (0.0065 - suckerIndex * 0.0007),
|
|
25362
|
+
});
|
|
25363
|
+
}
|
|
25364
|
+
}
|
|
25365
|
+
}
|
|
25366
|
+
/**
|
|
25367
|
+
* Draws one tiny projected surface spot by sampling local mesh tangents.
|
|
25368
|
+
*
|
|
25369
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25370
|
+
*/
|
|
25371
|
+
function drawProjectedSurfaceSpot(options) {
|
|
25372
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
|
|
25373
|
+
const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
|
|
25374
|
+
const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
|
|
25375
|
+
const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
|
|
25376
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
25377
|
+
if (sceneCenterPoint.z <= center.z - size * 0.012) {
|
|
25378
|
+
return;
|
|
25379
|
+
}
|
|
25380
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
25381
|
+
const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
25382
|
+
const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
25383
|
+
const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
|
|
25384
|
+
radiusScale *
|
|
25385
|
+
0.74, size * 0.003, size * 0.018);
|
|
25386
|
+
const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
|
|
25387
|
+
radiusScale *
|
|
25388
|
+
0.52, size * 0.0024, size * 0.014);
|
|
25389
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
25390
|
+
context.save();
|
|
25391
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
25392
|
+
context.rotate(rotation);
|
|
25393
|
+
context.beginPath();
|
|
25394
|
+
context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
|
|
25395
|
+
context.fillStyle = `${palette.highlight}73`;
|
|
25396
|
+
context.fill();
|
|
25397
|
+
context.strokeStyle = `${palette.highlight}99`;
|
|
25398
|
+
context.lineWidth = Math.max(0.7, size * 0.0028);
|
|
25399
|
+
context.stroke();
|
|
25400
|
+
context.restore();
|
|
25401
|
+
}
|
|
25402
|
+
/**
|
|
25403
|
+
* Resolves a signed angular distance from the source longitude to the target longitude.
|
|
25404
|
+
*
|
|
25405
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25406
|
+
*/
|
|
25407
|
+
function resolveSignedAngularDistance$1(sourceLongitude, targetLongitude) {
|
|
25408
|
+
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
25409
|
+
}
|
|
25410
|
+
/**
|
|
25411
|
+
* Smoothly maps a value between two bounds into `[0, 1]`.
|
|
25412
|
+
*
|
|
25413
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
25414
|
+
*/
|
|
25415
|
+
function smoothStep$1(edgeStart, edgeEnd, value) {
|
|
25416
|
+
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
25417
|
+
return progress * progress * (3 - 2 * progress);
|
|
25418
|
+
}
|
|
25419
|
+
|
|
25420
|
+
/* eslint-disable no-magic-numbers */
|
|
25421
|
+
/**
|
|
25422
|
+
* Light direction used by the continuous Octopus 3D 4 mesh shading.
|
|
25423
|
+
*
|
|
25424
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25425
|
+
*/
|
|
25426
|
+
const LIGHT_DIRECTION = normalizeVector3({
|
|
25427
|
+
x: 0.32,
|
|
25428
|
+
y: -0.66,
|
|
25429
|
+
z: 1,
|
|
25430
|
+
});
|
|
25431
|
+
/**
|
|
25432
|
+
* Rim-light direction used to brighten the side of the silhouette.
|
|
25433
|
+
*
|
|
25434
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25435
|
+
*/
|
|
25436
|
+
const RIM_LIGHT_DIRECTION = normalizeVector3({
|
|
25437
|
+
x: -0.7,
|
|
25438
|
+
y: -0.18,
|
|
25439
|
+
z: 0.55,
|
|
25440
|
+
});
|
|
25441
|
+
/**
|
|
25442
|
+
* Real-octopus tentacle count used by the continuous lower mesh.
|
|
25443
|
+
*
|
|
25444
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25445
|
+
*/
|
|
25446
|
+
const OCTOPUS_TENTACLE_COUNT = 8;
|
|
25447
|
+
/**
|
|
25448
|
+
* Number of seeded skin spots painted across the octopus body.
|
|
25449
|
+
*
|
|
25450
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25451
|
+
*/
|
|
25452
|
+
const SKIN_SPOT_COUNT = 14;
|
|
25453
|
+
/**
|
|
25454
|
+
* Cache keyed by the `createRandom` factory reference, stable per mounted `<Avatar/>` component.
|
|
25455
|
+
*
|
|
25456
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25457
|
+
*/
|
|
25458
|
+
const octopus3d4StableStateCache = new WeakMap();
|
|
25459
|
+
/**
|
|
25460
|
+
* Returns the stable per-avatar state, computing it on first access and caching afterwards.
|
|
25461
|
+
*
|
|
25462
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25463
|
+
*/
|
|
25464
|
+
function getOctopus3d4StableState(createRandom) {
|
|
25465
|
+
const cached = octopus3d4StableStateCache.get(createRandom);
|
|
25466
|
+
if (cached !== undefined) {
|
|
25467
|
+
return cached;
|
|
25468
|
+
}
|
|
25469
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
25470
|
+
const animationRandom = createRandom('octopus3d4-animation-profile');
|
|
25471
|
+
const eyeRandom = createRandom('octopus3d4-eye-profile');
|
|
25472
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
25473
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
25474
|
+
const state = {
|
|
25475
|
+
morphologyProfile,
|
|
25476
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
25477
|
+
leftEyePhaseOffset,
|
|
25478
|
+
rightEyePhaseOffset,
|
|
25479
|
+
tentacleProfiles: createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
25480
|
+
skinSpots: createBlobbySkinSpots(createRandom),
|
|
25481
|
+
};
|
|
25482
|
+
octopus3d4StableStateCache.set(createRandom, state);
|
|
25483
|
+
return state;
|
|
25484
|
+
}
|
|
25485
|
+
/**
|
|
25486
|
+
* Octopus 3D 4 avatar visual.
|
|
25487
|
+
*
|
|
25488
|
+
* @private built-in avatar visual
|
|
25489
|
+
*/
|
|
25490
|
+
const octopus3d4AvatarVisual = {
|
|
25491
|
+
id: 'octopus3d4',
|
|
25492
|
+
title: 'Octopus 3D 4',
|
|
25493
|
+
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.',
|
|
25494
|
+
isAnimated: true,
|
|
25495
|
+
supportsPointerTracking: true,
|
|
25496
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
25497
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
|
|
25498
|
+
const sceneCenterX = size * 0.5;
|
|
25499
|
+
const sceneCenterY = size * 0.535;
|
|
25500
|
+
const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
|
|
25501
|
+
const meshCenter = {
|
|
25502
|
+
x: interaction.bodyOffsetX * size * 0.052 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
|
|
25503
|
+
y: -size * 0.07 + interaction.bodyOffsetY * size * 0.028 + bob,
|
|
25504
|
+
z: interaction.intensity * size * 0.02,
|
|
25505
|
+
};
|
|
25506
|
+
const rotationY = -0.08 +
|
|
25507
|
+
Math.sin(timeMs / 2800 + animationPhase) * 0.04 +
|
|
25508
|
+
interaction.bodyOffsetX * 0.24 +
|
|
25509
|
+
interaction.gazeX * 0.98;
|
|
25510
|
+
const rotationX = -0.07 +
|
|
25511
|
+
Math.cos(timeMs / 3200 + animationPhase * 0.7) * 0.02 -
|
|
25512
|
+
interaction.bodyOffsetY * 0.08 -
|
|
25513
|
+
interaction.gazeY * 0.42;
|
|
25514
|
+
const surfaceOptions = {
|
|
25515
|
+
radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.12,
|
|
25516
|
+
radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1,
|
|
25517
|
+
radiusZ: size *
|
|
25518
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
25519
|
+
(1.04 + (morphologyProfile.body.horizontalStretch - 1) * 0.2),
|
|
25520
|
+
morphologyProfile,
|
|
25521
|
+
timeMs,
|
|
25522
|
+
animationPhase,
|
|
25523
|
+
tentacleProfiles,
|
|
25524
|
+
};
|
|
25525
|
+
const surfacePatches = resolveVisibleBlobbyContinuousPatches({
|
|
25526
|
+
...surfaceOptions,
|
|
25527
|
+
center: meshCenter,
|
|
25528
|
+
rotationX,
|
|
25529
|
+
rotationY,
|
|
25530
|
+
sceneCenterX,
|
|
25531
|
+
sceneCenterY,
|
|
25532
|
+
size,
|
|
25533
|
+
palette,
|
|
25534
|
+
});
|
|
25535
|
+
const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.4 - 0.04, -0.24, 0.08);
|
|
25536
|
+
const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.0, 0.18, 0.32);
|
|
25537
|
+
const mouthLatitude = clampNumber$1(eyeLatitude + 0.21 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
|
|
25538
|
+
const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
|
|
25539
|
+
const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
|
|
25540
|
+
const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
|
|
25541
|
+
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
25542
|
+
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
25543
|
+
drawAvatarFrame(context, size, palette);
|
|
25544
|
+
drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
25545
|
+
drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile);
|
|
25546
|
+
for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
25547
|
+
drawBlobbyContinuousSurfacePatch(context, surfacePatch);
|
|
25548
|
+
}
|
|
25549
|
+
drawBlobbySkinSpots({
|
|
25550
|
+
context,
|
|
25551
|
+
surfaceOptions,
|
|
25552
|
+
center: meshCenter,
|
|
25553
|
+
rotationX,
|
|
25554
|
+
rotationY,
|
|
25555
|
+
sceneCenterX,
|
|
25556
|
+
sceneCenterY,
|
|
25557
|
+
size,
|
|
25558
|
+
palette,
|
|
25559
|
+
skinSpots,
|
|
25560
|
+
});
|
|
25561
|
+
drawBlobbyContinuousCurrents({
|
|
25562
|
+
context,
|
|
25563
|
+
surfaceOptions,
|
|
25564
|
+
center: meshCenter,
|
|
25565
|
+
rotationX,
|
|
25566
|
+
rotationY,
|
|
25567
|
+
sceneCenterX,
|
|
25568
|
+
sceneCenterY,
|
|
25569
|
+
size,
|
|
25570
|
+
palette,
|
|
25571
|
+
morphologyProfile,
|
|
25572
|
+
timeMs,
|
|
25573
|
+
animationPhase,
|
|
25574
|
+
});
|
|
25575
|
+
drawBlobbyContinuousSuckers({
|
|
25576
|
+
context,
|
|
25577
|
+
surfaceOptions,
|
|
25578
|
+
center: meshCenter,
|
|
25579
|
+
rotationX,
|
|
25580
|
+
rotationY,
|
|
25581
|
+
sceneCenterX,
|
|
25582
|
+
sceneCenterY,
|
|
25583
|
+
size,
|
|
25584
|
+
palette,
|
|
25585
|
+
});
|
|
25586
|
+
drawBlobbyContinuousGloss({
|
|
25587
|
+
context,
|
|
25588
|
+
surfaceOptions,
|
|
25589
|
+
center: meshCenter,
|
|
25590
|
+
rotationX,
|
|
25591
|
+
rotationY,
|
|
25592
|
+
sceneCenterX,
|
|
25593
|
+
sceneCenterY,
|
|
25594
|
+
size,
|
|
25595
|
+
palette,
|
|
25596
|
+
});
|
|
25597
|
+
drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
25598
|
+
drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
25599
|
+
drawProjectedOrganicMouth(context, [
|
|
25600
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
25601
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
25602
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
|
|
25603
|
+
], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
|
|
25604
|
+
},
|
|
25605
|
+
};
|
|
25606
|
+
/**
|
|
25607
|
+
* Creates seeded tentacle-lobe profiles around the visible lower octopus body.
|
|
25608
|
+
*
|
|
25609
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25610
|
+
*/
|
|
25611
|
+
function createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
25612
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
|
|
25613
|
+
const tentacleRandom = createRandom(`octopus3d4-tentacle-${tentacleIndex}`);
|
|
25614
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
|
|
25615
|
+
return {
|
|
25616
|
+
centerLongitude: -Math.PI * 0.9 +
|
|
25617
|
+
progress * Math.PI * 1.8 +
|
|
25618
|
+
(tentacleRandom() - 0.5) * (0.06 + morphologyProfile.tentacles.rootSpreadScale * 0.025),
|
|
25619
|
+
widthScale: 0.92 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.18,
|
|
25620
|
+
lengthScale: 0.9 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.24,
|
|
25621
|
+
swayScale: 0.86 + tentacleRandom() * 0.4 + (morphologyProfile.tentacles.swayScale - 1) * 0.22,
|
|
25622
|
+
depthScale: 0.9 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.22,
|
|
25623
|
+
curlScale: 0.62 + tentacleRandom() * 0.46,
|
|
25624
|
+
primaryPhase: tentacleRandom() * Math.PI * 2,
|
|
25625
|
+
secondaryPhase: tentacleRandom() * Math.PI * 2,
|
|
25626
|
+
suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
|
|
25627
|
+
};
|
|
25628
|
+
});
|
|
25629
|
+
}
|
|
25630
|
+
/**
|
|
25631
|
+
* Creates seeded skin pigment spots distributed across the upper octopus mesh.
|
|
25632
|
+
*
|
|
25633
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25634
|
+
*/
|
|
25635
|
+
function createBlobbySkinSpots(createRandom) {
|
|
25636
|
+
const spotRandom = createRandom('octopus3d4-skin-spots');
|
|
25637
|
+
return Array.from({ length: SKIN_SPOT_COUNT }, () => ({
|
|
25638
|
+
latitude: -0.45 + spotRandom() * 0.7,
|
|
25639
|
+
longitude: -0.5 + spotRandom() * 1.0,
|
|
25640
|
+
radiusScale: 0.0028 + spotRandom() * 0.0052,
|
|
25641
|
+
opacity: 0.16 + spotRandom() * 0.22,
|
|
25642
|
+
}));
|
|
25643
|
+
}
|
|
25644
|
+
/**
|
|
25645
|
+
* Draws the soft underwater atmosphere behind the continuous octopus mesh.
|
|
25646
|
+
*
|
|
25647
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25648
|
+
*/
|
|
25649
|
+
function drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
25650
|
+
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));
|
|
25651
|
+
glowGradient.addColorStop(0, `${palette.highlight}74`);
|
|
25652
|
+
glowGradient.addColorStop(0.32, `${palette.accent}32`);
|
|
25653
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
25654
|
+
context.fillStyle = glowGradient;
|
|
25655
|
+
context.fillRect(0, 0, size, size);
|
|
25656
|
+
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);
|
|
25657
|
+
lowerGradient.addColorStop(0, `${palette.secondary}28`);
|
|
25658
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
25659
|
+
context.fillStyle = lowerGradient;
|
|
25660
|
+
context.fillRect(0, 0, size, size);
|
|
25661
|
+
}
|
|
25662
|
+
/**
|
|
25663
|
+
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
25664
|
+
*
|
|
25665
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25666
|
+
*/
|
|
25667
|
+
function drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
25668
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.046;
|
|
25669
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
25670
|
+
const rx = size * (0.2 + morphologyProfile.tentacles.rootSpreadScale * 0.024 + interaction.intensity * 0.022);
|
|
25671
|
+
const ry = size * 0.062;
|
|
25672
|
+
context.save();
|
|
25673
|
+
context.translate(cx, cy);
|
|
25674
|
+
context.scale(1, ry / rx);
|
|
25675
|
+
const blurRadius = rx * 1.42;
|
|
25676
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
25677
|
+
shadowGradient.addColorStop(0, `${palette.shadow}82`);
|
|
25678
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}4a`);
|
|
25679
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1c`);
|
|
25680
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
25681
|
+
context.fillStyle = shadowGradient;
|
|
25682
|
+
context.beginPath();
|
|
25683
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
25684
|
+
context.fill();
|
|
25685
|
+
context.restore();
|
|
25686
|
+
}
|
|
25687
|
+
/**
|
|
25688
|
+
* Number of latitude segments used by the continuous Octopus 3D 4 mesh.
|
|
25689
|
+
*
|
|
25690
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25691
|
+
*/
|
|
25692
|
+
const LATITUDE_PATCH_COUNT = 20;
|
|
25693
|
+
/**
|
|
25694
|
+
* Number of longitude segments used by the continuous Octopus 3D 4 mesh.
|
|
25695
|
+
*
|
|
25696
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25697
|
+
*/
|
|
25698
|
+
const LONGITUDE_PATCH_COUNT = 48;
|
|
25699
|
+
/**
|
|
25700
|
+
* Resolves visible projected patches for the continuous Octopus 3D 4 mesh.
|
|
25701
|
+
*
|
|
25702
|
+
* Within a single frame, mesh corner samples and longitude-only computations (tentacle
|
|
25703
|
+
* influence and lobe wave) are quantized to the patch grid and computed once each rather
|
|
25704
|
+
* than re-evaluated for every patch corner.
|
|
25705
|
+
*
|
|
25706
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25707
|
+
*/
|
|
25708
|
+
function resolveVisibleBlobbyContinuousPatches(options) {
|
|
25709
|
+
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
25710
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT;
|
|
25711
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT;
|
|
25712
|
+
const surfacePatches = [];
|
|
25713
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
25714
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
25715
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
25716
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
25717
|
+
}
|
|
25718
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
25719
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
25720
|
+
}
|
|
25721
|
+
const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
|
|
25722
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
25723
|
+
const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
|
|
25724
|
+
const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
25725
|
+
const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
25726
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
25727
|
+
const cornerLongitude = longitudeBoundaries[boundaryIndex];
|
|
25728
|
+
cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveBlobbyTentacleInfluence(options, cornerLongitude);
|
|
25729
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveBlobbyLobeWave(options, cornerLongitude);
|
|
25730
|
+
}
|
|
25731
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
25732
|
+
const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
|
|
25733
|
+
cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyTentacleInfluence(options, patchCenterLongitude);
|
|
25734
|
+
cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyLobeWave(options, patchCenterLongitude);
|
|
25735
|
+
cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
|
|
25736
|
+
}
|
|
25737
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
25738
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
25739
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
25740
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
25741
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
25742
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
25743
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
25744
|
+
const cornerSample = sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
25745
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
25746
|
+
}
|
|
25747
|
+
}
|
|
25748
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
25749
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
25750
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
25751
|
+
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
25752
|
+
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
25753
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
25754
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
25755
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
25756
|
+
const transformedCorners = [
|
|
25757
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
25758
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
25759
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
25760
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
25761
|
+
];
|
|
25762
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
25763
|
+
if (surfaceNormal.z <= 0.006) {
|
|
25764
|
+
continue;
|
|
25765
|
+
}
|
|
25766
|
+
const projectedCorners = [
|
|
25767
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
25768
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
25769
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
25770
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
25771
|
+
];
|
|
25772
|
+
const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
|
|
25773
|
+
const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
|
|
25774
|
+
surfacePatches.push({
|
|
25775
|
+
corners: projectedCorners,
|
|
25776
|
+
averageDepth: (transformedCorners[0].z +
|
|
25777
|
+
transformedCorners[1].z +
|
|
25778
|
+
transformedCorners[2].z +
|
|
25779
|
+
transformedCorners[3].z) /
|
|
25780
|
+
4,
|
|
25781
|
+
lightIntensity,
|
|
25782
|
+
rimLightIntensity,
|
|
25783
|
+
fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
25784
|
+
outlineColor: verticalProgress < 0.52 ? `${palette.highlight}5c` : `${palette.shadow}66`,
|
|
25785
|
+
});
|
|
25786
|
+
}
|
|
25787
|
+
}
|
|
25788
|
+
return surfacePatches;
|
|
25789
|
+
}
|
|
25790
|
+
/**
|
|
25791
|
+
* Samples one point on the continuous Octopus 3D 4 surface.
|
|
25792
|
+
*
|
|
25793
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25794
|
+
*/
|
|
25795
|
+
function sampleBlobbyContinuousSurfacePoint(options, latitude, longitude) {
|
|
25796
|
+
return sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, resolveBlobbyTentacleInfluence(options, longitude), resolveBlobbyLobeWave(options, longitude));
|
|
25797
|
+
}
|
|
25798
|
+
/**
|
|
25799
|
+
* Samples one point on the continuous Octopus 3D 4 surface using precomputed longitude-only values.
|
|
25800
|
+
*
|
|
25801
|
+
* The patch loop quantizes the mesh into a fixed corner grid, so the same longitude is reused
|
|
25802
|
+
* across every latitude row and each tentacle/lobe value is computed once per frame instead
|
|
25803
|
+
* of `latitudePatchCount * 4` times.
|
|
25804
|
+
*
|
|
25805
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25806
|
+
*/
|
|
25807
|
+
function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
|
|
25808
|
+
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
25809
|
+
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
25810
|
+
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
25811
|
+
const upperBlend = Math.pow(1 - verticalProgress, 1.32);
|
|
25812
|
+
const lowerBlend = smoothStep(0.34, 1, verticalProgress);
|
|
25813
|
+
const tipBlend = smoothStep(0.66, 1, verticalProgress);
|
|
25814
|
+
const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
|
|
25815
|
+
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.26 + tipBlend * 0.22);
|
|
25816
|
+
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
25817
|
+
animationPhase * 0.6 +
|
|
25818
|
+
timeMs / (1700 + morphologyProfile.body.lobeCount * 28)) *
|
|
25819
|
+
(0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.78) *
|
|
25820
|
+
(0.32 + lowerBlend * 0.7);
|
|
25821
|
+
const primaryTentacleWave = Math.sin(timeMs / 740 + tentacleInfluence.primaryPhase + verticalProgress * 2.6) *
|
|
25822
|
+
lowerBlend *
|
|
25823
|
+
tentacleInfluence.core *
|
|
25824
|
+
tentacleInfluence.swayScale;
|
|
25825
|
+
const secondaryTentacleWave = Math.sin(timeMs / 470 + tentacleInfluence.secondaryPhase + verticalProgress * 4.2) *
|
|
25826
|
+
lowerBlend *
|
|
25827
|
+
tentacleInfluence.core *
|
|
25828
|
+
tentacleInfluence.swayScale *
|
|
25829
|
+
0.42;
|
|
25830
|
+
const tentacleCurl = Math.sin(timeMs / 1180 + tentacleInfluence.primaryPhase * 0.6 + verticalProgress * 3.2) *
|
|
25831
|
+
tipBlend *
|
|
25832
|
+
tentacleInfluence.core *
|
|
25833
|
+
tentacleInfluence.curlScale *
|
|
25834
|
+
0.36;
|
|
25835
|
+
const horizontalScale = 1.06 +
|
|
25836
|
+
mantleRipple +
|
|
25837
|
+
lowerBlend * (0.18 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.12) +
|
|
25838
|
+
lowerBlend * tentacleInfluence.core * (0.22 + lowerLobeWave * 0.14) -
|
|
25839
|
+
upperBlend * 0.08;
|
|
25840
|
+
const depthScale = 1.08 +
|
|
25841
|
+
upperBlend * 0.16 +
|
|
25842
|
+
Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
|
|
25843
|
+
lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
|
|
25844
|
+
Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
|
|
25845
|
+
const tentacleTubeRadius = lowerBlend *
|
|
25846
|
+
tentacleInfluence.core *
|
|
25847
|
+
(0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) *
|
|
25848
|
+
radiusX;
|
|
25849
|
+
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
25850
|
+
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
|
|
25851
|
+
const lowerDrop = lowerBlend *
|
|
25852
|
+
radiusY *
|
|
25853
|
+
(0.2 +
|
|
25854
|
+
tentacleInfluence.core *
|
|
25855
|
+
(0.42 +
|
|
25856
|
+
tentacleInfluence.lengthScale * 0.24 +
|
|
25857
|
+
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
|
|
25858
|
+
const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
|
|
25859
|
+
return {
|
|
25860
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX +
|
|
25861
|
+
combinedTentacleSway +
|
|
25862
|
+
tentacleCurl * radiusX * 0.18,
|
|
25863
|
+
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
|
|
25864
|
+
upperBlend * radiusY * 0.12 +
|
|
25865
|
+
lowerDrop +
|
|
25866
|
+
Math.sin(timeMs / 1380 + animationPhase + latitude * 1.5) * lowerBlend * radiusY * 0.022 +
|
|
25867
|
+
Math.cos(timeMs / 820 + tentacleInfluence.primaryPhase) *
|
|
25868
|
+
lowerBlend *
|
|
25869
|
+
tipBlend *
|
|
25870
|
+
tentacleInfluence.core *
|
|
25871
|
+
radiusY *
|
|
25872
|
+
0.04,
|
|
25873
|
+
z: Math.cos(effectiveLongitude) * planarRadiusZ +
|
|
25874
|
+
Math.cos(timeMs / 960 + tentacleInfluence.primaryPhase + verticalProgress) *
|
|
25875
|
+
lowerBlend *
|
|
25876
|
+
tentacleInfluence.core *
|
|
25877
|
+
radiusZ *
|
|
25878
|
+
0.044 +
|
|
25879
|
+
tentacleCurl * radiusZ * 0.14,
|
|
25880
|
+
};
|
|
25881
|
+
}
|
|
25882
|
+
/**
|
|
25883
|
+
* Blends nearby seeded tentacle profiles at one mesh longitude.
|
|
25884
|
+
*
|
|
25885
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25886
|
+
*/
|
|
25887
|
+
function resolveBlobbyTentacleInfluence(options, longitude) {
|
|
25888
|
+
let totalWeight = 0;
|
|
25889
|
+
let weightedSin = 0;
|
|
25890
|
+
let weightedCos = 0;
|
|
25891
|
+
let weightedWidthScale = 0;
|
|
25892
|
+
let weightedLengthScale = 0;
|
|
25893
|
+
let weightedSwayScale = 0;
|
|
25894
|
+
let weightedDepthScale = 0;
|
|
25895
|
+
let weightedCurlScale = 0;
|
|
25896
|
+
let weightedPrimaryPhase = 0;
|
|
25897
|
+
let weightedSecondaryPhase = 0;
|
|
25898
|
+
for (const tentacleProfile of options.tentacleProfiles) {
|
|
25899
|
+
const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
|
|
25900
|
+
const width = 0.22 * tentacleProfile.widthScale;
|
|
25901
|
+
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
25902
|
+
totalWeight += weight;
|
|
25903
|
+
weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
|
|
25904
|
+
weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
|
|
25905
|
+
weightedWidthScale += tentacleProfile.widthScale * weight;
|
|
25906
|
+
weightedLengthScale += tentacleProfile.lengthScale * weight;
|
|
25907
|
+
weightedSwayScale += tentacleProfile.swayScale * weight;
|
|
25908
|
+
weightedDepthScale += tentacleProfile.depthScale * weight;
|
|
25909
|
+
weightedCurlScale += tentacleProfile.curlScale * weight;
|
|
25910
|
+
weightedPrimaryPhase += tentacleProfile.primaryPhase * weight;
|
|
25911
|
+
weightedSecondaryPhase += tentacleProfile.secondaryPhase * weight;
|
|
25912
|
+
}
|
|
25913
|
+
if (totalWeight < 0.0001) {
|
|
25914
|
+
return {
|
|
25915
|
+
core: 0,
|
|
25916
|
+
centerLongitude: longitude,
|
|
25917
|
+
widthScale: 1,
|
|
25918
|
+
lengthScale: 1,
|
|
25919
|
+
swayScale: 1,
|
|
25920
|
+
depthScale: 1,
|
|
25921
|
+
curlScale: 1,
|
|
25922
|
+
primaryPhase: 0,
|
|
25923
|
+
secondaryPhase: 0,
|
|
25924
|
+
};
|
|
25925
|
+
}
|
|
25926
|
+
return {
|
|
25927
|
+
core: clampNumber$1(totalWeight, 0, 1),
|
|
25928
|
+
centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
|
|
25929
|
+
widthScale: weightedWidthScale / totalWeight,
|
|
25930
|
+
lengthScale: weightedLengthScale / totalWeight,
|
|
25931
|
+
swayScale: weightedSwayScale / totalWeight,
|
|
25932
|
+
depthScale: weightedDepthScale / totalWeight,
|
|
25933
|
+
curlScale: weightedCurlScale / totalWeight,
|
|
25934
|
+
primaryPhase: weightedPrimaryPhase / totalWeight,
|
|
25935
|
+
secondaryPhase: weightedSecondaryPhase / totalWeight,
|
|
25936
|
+
};
|
|
25937
|
+
}
|
|
25938
|
+
/**
|
|
25939
|
+
* Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
|
|
25940
|
+
*
|
|
25941
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25942
|
+
*/
|
|
25943
|
+
function resolveBlobbyLobeWave(options, longitude) {
|
|
25944
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
25945
|
+
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
|
|
25946
|
+
animationPhase +
|
|
25947
|
+
timeMs / (940 + morphologyProfile.body.lobeCount * 18)) +
|
|
25948
|
+
1) /
|
|
25949
|
+
2);
|
|
25950
|
+
}
|
|
25951
|
+
/**
|
|
25952
|
+
* Resolves one base fill tone for a patch on the continuous octopus mesh.
|
|
25953
|
+
*
|
|
25954
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25955
|
+
*/
|
|
25956
|
+
function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
|
|
25957
|
+
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
|
|
25958
|
+
if (tonalProgress < 0.12) {
|
|
25959
|
+
return palette.highlight;
|
|
25960
|
+
}
|
|
25961
|
+
if (tonalProgress < 0.3) {
|
|
25962
|
+
return palette.secondary;
|
|
25963
|
+
}
|
|
25964
|
+
if (tonalProgress < 0.72) {
|
|
25965
|
+
return forwardness > 0.56 ? palette.secondary : palette.primary;
|
|
25966
|
+
}
|
|
25967
|
+
return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
|
|
25968
|
+
}
|
|
25969
|
+
/**
|
|
25970
|
+
* Draws one projected mesh patch with multi-pass shading and a soft edge.
|
|
25971
|
+
*
|
|
25972
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25973
|
+
*/
|
|
25974
|
+
function drawBlobbyContinuousSurfacePatch(context, surfacePatch) {
|
|
25975
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
25976
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
25977
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.2 * surfacePatch.lightIntensity})`);
|
|
25978
|
+
}
|
|
25979
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
25980
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.26 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
25981
|
+
}
|
|
25982
|
+
if (surfacePatch.rimLightIntensity > 0.04) {
|
|
25983
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 247, 230, ${0.22 * surfacePatch.rimLightIntensity})`);
|
|
25984
|
+
}
|
|
25985
|
+
context.save();
|
|
25986
|
+
context.beginPath();
|
|
25987
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
25988
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
25989
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
25990
|
+
}
|
|
25991
|
+
context.closePath();
|
|
25992
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
25993
|
+
context.lineWidth = Math.max(0.6, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0026);
|
|
25994
|
+
context.lineJoin = 'round';
|
|
25995
|
+
context.stroke();
|
|
25996
|
+
context.restore();
|
|
25997
|
+
}
|
|
25998
|
+
/**
|
|
25999
|
+
* Draws projected mantle-current lines on the front of the mesh.
|
|
26000
|
+
*
|
|
26001
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26002
|
+
*/
|
|
26003
|
+
function drawBlobbyContinuousCurrents(options) {
|
|
26004
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
|
|
26005
|
+
const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
|
|
26006
|
+
const centerIndex = (currentCount - 1) / 2;
|
|
26007
|
+
context.save();
|
|
26008
|
+
context.lineCap = 'round';
|
|
26009
|
+
context.lineJoin = 'round';
|
|
26010
|
+
for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
|
|
26011
|
+
const baseLongitude = (currentIndex - centerIndex) * 0.15;
|
|
26012
|
+
const projectedPoints = [];
|
|
26013
|
+
for (let sampleIndex = 0; sampleIndex < 9; sampleIndex++) {
|
|
26014
|
+
const progress = sampleIndex / 8;
|
|
26015
|
+
const latitude = -0.48 + progress * 0.78;
|
|
26016
|
+
const longitude = baseLongitude + Math.sin(timeMs / 1140 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.038;
|
|
26017
|
+
const scenePoint = transformScenePoint(sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
|
|
26018
|
+
if (scenePoint.z > center.z - size * 0.016) {
|
|
26019
|
+
projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
26020
|
+
}
|
|
26021
|
+
}
|
|
26022
|
+
if (projectedPoints.length < 3) {
|
|
26023
|
+
continue;
|
|
26024
|
+
}
|
|
26025
|
+
context.beginPath();
|
|
26026
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
26027
|
+
for (const projectedPoint of projectedPoints.slice(1)) {
|
|
26028
|
+
context.lineTo(projectedPoint.x, projectedPoint.y);
|
|
26029
|
+
}
|
|
26030
|
+
context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
|
|
26031
|
+
context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
|
|
26032
|
+
context.stroke();
|
|
26033
|
+
}
|
|
26034
|
+
context.restore();
|
|
26035
|
+
}
|
|
26036
|
+
/**
|
|
26037
|
+
* Draws small projected sucker highlights on the waving lower mesh lobes.
|
|
26038
|
+
*
|
|
26039
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26040
|
+
*/
|
|
26041
|
+
function drawBlobbyContinuousSuckers(options) {
|
|
26042
|
+
const { surfaceOptions, size } = options;
|
|
26043
|
+
const { timeMs } = surfaceOptions;
|
|
26044
|
+
for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
|
|
26045
|
+
if (Math.cos(tentacleProfile.centerLongitude) < -0.16) {
|
|
26046
|
+
continue;
|
|
26047
|
+
}
|
|
26048
|
+
for (let suckerIndex = 0; suckerIndex < 4; suckerIndex++) {
|
|
26049
|
+
const latitude = 0.5 + suckerIndex * 0.12;
|
|
26050
|
+
const sideOffset = tentacleProfile.suckerSide * (0.038 + suckerIndex * 0.014) * tentacleProfile.widthScale;
|
|
26051
|
+
const waveOffset = Math.sin(timeMs / 880 + tentacleProfile.primaryPhase + suckerIndex * 0.78) * 0.02;
|
|
26052
|
+
drawBlobbyContinuousSurfaceSpot({
|
|
26053
|
+
...options,
|
|
26054
|
+
latitude,
|
|
26055
|
+
longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
|
|
26056
|
+
radiusScale: size * (0.0068 - suckerIndex * 0.0006),
|
|
26057
|
+
});
|
|
26058
|
+
}
|
|
26059
|
+
}
|
|
26060
|
+
}
|
|
26061
|
+
/**
|
|
26062
|
+
* Draws seeded pigment spots across the upper mesh for a richer skin texture.
|
|
26063
|
+
*
|
|
26064
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26065
|
+
*/
|
|
26066
|
+
function drawBlobbySkinSpots(options) {
|
|
26067
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, skinSpots, } = options;
|
|
26068
|
+
for (const skinSpot of skinSpots) {
|
|
26069
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, skinSpot.latitude, skinSpot.longitude);
|
|
26070
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
26071
|
+
if (sceneCenterPoint.z <= center.z - size * 0.01) {
|
|
26072
|
+
continue;
|
|
26073
|
+
}
|
|
26074
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
26075
|
+
const spotRadius = size * skinSpot.radiusScale;
|
|
26076
|
+
context.save();
|
|
26077
|
+
context.beginPath();
|
|
26078
|
+
context.arc(projectedCenterPoint.x, projectedCenterPoint.y, spotRadius, 0, Math.PI * 2);
|
|
26079
|
+
context.fillStyle = `${palette.shadow}${formatAlphaHex(skinSpot.opacity)}`;
|
|
26080
|
+
context.fill();
|
|
26081
|
+
context.restore();
|
|
26082
|
+
}
|
|
26083
|
+
}
|
|
26084
|
+
/**
|
|
26085
|
+
* Draws one tiny projected surface spot by sampling local mesh tangents.
|
|
26086
|
+
*
|
|
26087
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26088
|
+
*/
|
|
26089
|
+
function drawBlobbyContinuousSurfaceSpot(options) {
|
|
26090
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
|
|
26091
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude);
|
|
26092
|
+
const localHorizontal = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
|
|
26093
|
+
const localVertical = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
|
|
26094
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
26095
|
+
if (sceneCenterPoint.z <= center.z - size * 0.012) {
|
|
26096
|
+
return;
|
|
26097
|
+
}
|
|
26098
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
26099
|
+
const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
26100
|
+
const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
26101
|
+
const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
|
|
26102
|
+
radiusScale *
|
|
26103
|
+
0.78, size * 0.003, size * 0.02);
|
|
26104
|
+
const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
|
|
26105
|
+
radiusScale *
|
|
26106
|
+
0.54, size * 0.0024, size * 0.015);
|
|
26107
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
26108
|
+
context.save();
|
|
26109
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
26110
|
+
context.rotate(rotation);
|
|
26111
|
+
context.beginPath();
|
|
26112
|
+
context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
|
|
26113
|
+
context.fillStyle = `${palette.highlight}80`;
|
|
26114
|
+
context.fill();
|
|
26115
|
+
context.strokeStyle = `${palette.highlight}a8`;
|
|
26116
|
+
context.lineWidth = Math.max(0.7, size * 0.0028);
|
|
26117
|
+
context.stroke();
|
|
26118
|
+
context.restore();
|
|
26119
|
+
}
|
|
26120
|
+
/**
|
|
26121
|
+
* Draws a soft, slowly drifting gloss highlight on the front of the mesh.
|
|
26122
|
+
*
|
|
26123
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26124
|
+
*/
|
|
26125
|
+
function drawBlobbyContinuousGloss(options) {
|
|
26126
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size } = options;
|
|
26127
|
+
const { timeMs, animationPhase } = surfaceOptions;
|
|
26128
|
+
const glossLatitude = -0.3 + Math.sin(timeMs / 2700 + animationPhase) * 0.04;
|
|
26129
|
+
const glossLongitude = -0.18 + Math.cos(timeMs / 2300 + animationPhase * 0.8) * 0.05;
|
|
26130
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, glossLatitude, glossLongitude);
|
|
26131
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
26132
|
+
if (sceneCenterPoint.z <= center.z) {
|
|
26133
|
+
return;
|
|
26134
|
+
}
|
|
26135
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
26136
|
+
const glossRadius = size * 0.058;
|
|
26137
|
+
context.save();
|
|
26138
|
+
const glossGradient = context.createRadialGradient(projectedCenterPoint.x - glossRadius * 0.3, projectedCenterPoint.y - glossRadius * 0.4, glossRadius * 0.04, projectedCenterPoint.x, projectedCenterPoint.y, glossRadius);
|
|
26139
|
+
glossGradient.addColorStop(0, 'rgba(255, 255, 255, 0.34)');
|
|
26140
|
+
glossGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.1)');
|
|
26141
|
+
glossGradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
|
|
26142
|
+
context.fillStyle = glossGradient;
|
|
26143
|
+
context.beginPath();
|
|
26144
|
+
context.ellipse(projectedCenterPoint.x, projectedCenterPoint.y, glossRadius * 1.05, glossRadius * 0.78, 0, 0, Math.PI * 2);
|
|
26145
|
+
context.fill();
|
|
26146
|
+
context.restore();
|
|
26147
|
+
}
|
|
26148
|
+
/**
|
|
26149
|
+
* Resolves a signed angular distance from the source longitude to the target longitude.
|
|
26150
|
+
*
|
|
26151
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26152
|
+
*/
|
|
26153
|
+
function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
|
|
26154
|
+
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
26155
|
+
}
|
|
26156
|
+
/**
|
|
26157
|
+
* Smoothly maps a value between two bounds into `[0, 1]`.
|
|
26158
|
+
*
|
|
26159
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26160
|
+
*/
|
|
26161
|
+
function smoothStep(edgeStart, edgeEnd, value) {
|
|
26162
|
+
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
26163
|
+
return progress * progress * (3 - 2 * progress);
|
|
26164
|
+
}
|
|
26165
|
+
/**
|
|
26166
|
+
* Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
|
|
26167
|
+
*
|
|
26168
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26169
|
+
*/
|
|
26170
|
+
function formatAlphaHex(opacity) {
|
|
26171
|
+
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
26172
|
+
.toString(16)
|
|
26173
|
+
.padStart(2, '0');
|
|
26174
|
+
}
|
|
26175
|
+
|
|
24624
26176
|
/* eslint-disable no-magic-numbers */
|
|
24625
26177
|
/**
|
|
24626
26178
|
* Octopus avatar visual.
|
|
@@ -25391,6 +26943,8 @@ const AVATAR_VISUALS = [
|
|
|
25391
26943
|
octopus3AvatarVisual,
|
|
25392
26944
|
octopus3dAvatarVisual,
|
|
25393
26945
|
octopus3d2AvatarVisual,
|
|
26946
|
+
octopus3d3AvatarVisual,
|
|
26947
|
+
octopus3d4AvatarVisual,
|
|
25394
26948
|
asciiOctopusAvatarVisual,
|
|
25395
26949
|
minecraftAvatarVisual,
|
|
25396
26950
|
minecraft2AvatarVisual,
|
|
@@ -26070,6 +27624,70 @@ class MetaLinkCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
26070
27624
|
}
|
|
26071
27625
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
26072
27626
|
|
|
27627
|
+
/**
|
|
27628
|
+
* META VISIBILITY commitment definition.
|
|
27629
|
+
*
|
|
27630
|
+
* The `META VISIBILITY` commitment stores whether an agent is public, private, or unlisted.
|
|
27631
|
+
* Agents Server mirrors this value into the database for efficient filtering, but the book
|
|
27632
|
+
* commitment remains the editable source of truth.
|
|
27633
|
+
*
|
|
27634
|
+
* @private Metadata-only commitment used by Agents Server.
|
|
27635
|
+
*/
|
|
27636
|
+
class MetaVisibilityCommitmentDefinition extends BaseCommitmentDefinition {
|
|
27637
|
+
constructor() {
|
|
27638
|
+
super('META VISIBILITY');
|
|
27639
|
+
}
|
|
27640
|
+
/**
|
|
27641
|
+
* Short one-line description of META VISIBILITY.
|
|
27642
|
+
*/
|
|
27643
|
+
get description() {
|
|
27644
|
+
return 'Set whether the agent is private, unlisted, or public.';
|
|
27645
|
+
}
|
|
27646
|
+
/**
|
|
27647
|
+
* Icon for this commitment.
|
|
27648
|
+
*/
|
|
27649
|
+
get icon() {
|
|
27650
|
+
return '👁️';
|
|
27651
|
+
}
|
|
27652
|
+
/**
|
|
27653
|
+
* Markdown documentation for META VISIBILITY commitment.
|
|
27654
|
+
*/
|
|
27655
|
+
get documentation() {
|
|
27656
|
+
return spaceTrim$1(`
|
|
27657
|
+
# META VISIBILITY
|
|
27658
|
+
|
|
27659
|
+
Sets the agent visibility used by Agents Server.
|
|
27660
|
+
|
|
27661
|
+
## Allowed values
|
|
27662
|
+
|
|
27663
|
+
- \`PRIVATE\` - accessible only to signed-in users with access.
|
|
27664
|
+
- \`UNLISTED\` - accessible by direct link but hidden from public listings.
|
|
27665
|
+
- \`PUBLIC\` - visible in public listings and accessible by anyone.
|
|
27666
|
+
|
|
27667
|
+
## Key aspects
|
|
27668
|
+
|
|
27669
|
+
- Does not modify the agent's behavior, system message, or tools.
|
|
27670
|
+
- Whitespace and letter case are normalized when persisted.
|
|
27671
|
+
- If multiple \`META VISIBILITY\` commitments are present, persistence keeps one normalized value.
|
|
27672
|
+
- Agents Server mirrors the value into the database for filtering, but the book is the source of truth.
|
|
27673
|
+
|
|
27674
|
+
## Example
|
|
27675
|
+
|
|
27676
|
+
\`\`\`book
|
|
27677
|
+
Helpful Assistant
|
|
27678
|
+
|
|
27679
|
+
GOAL Be helpful and friendly.
|
|
27680
|
+
META VISIBILITY PUBLIC
|
|
27681
|
+
\`\`\`
|
|
27682
|
+
`);
|
|
27683
|
+
}
|
|
27684
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
27685
|
+
// META VISIBILITY is metadata only and does not alter model requirements.
|
|
27686
|
+
return requirements;
|
|
27687
|
+
}
|
|
27688
|
+
}
|
|
27689
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
27690
|
+
|
|
26073
27691
|
/**
|
|
26074
27692
|
* META VOICE commitment definition
|
|
26075
27693
|
*
|
|
@@ -27679,7 +29297,15 @@ function buildTeammateMetadata(entry) {
|
|
|
27679
29297
|
* Builds the teammate request text, optionally including context.
|
|
27680
29298
|
*/
|
|
27681
29299
|
function buildTeammateRequest(message, context) {
|
|
27682
|
-
|
|
29300
|
+
if (!context) {
|
|
29301
|
+
return message;
|
|
29302
|
+
}
|
|
29303
|
+
return spaceTrim$1((block) => `
|
|
29304
|
+
${block(message)}
|
|
29305
|
+
|
|
29306
|
+
Context:
|
|
29307
|
+
${block(context)}
|
|
29308
|
+
`);
|
|
27683
29309
|
}
|
|
27684
29310
|
/**
|
|
27685
29311
|
* Builds a minimal chat prompt for teammate calls.
|
|
@@ -31505,7 +33131,11 @@ function createUseProjectToolFunctions() {
|
|
|
31505
33131
|
const lineRangedContent = applyOptionalLineRange(decodedContent, args.startLine, args.endLine);
|
|
31506
33132
|
const wasCharacterTruncated = lineRangedContent.length > MAX_PROJECT_FILE_CONTENT_CHARACTERS;
|
|
31507
33133
|
const contentToReturn = wasCharacterTruncated
|
|
31508
|
-
?
|
|
33134
|
+
? spaceTrim$1((block) => `
|
|
33135
|
+
${block(lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS))}
|
|
33136
|
+
|
|
33137
|
+
[...truncated...]
|
|
33138
|
+
`)
|
|
31509
33139
|
: lineRangedContent;
|
|
31510
33140
|
const wasTruncated = decoded.isTruncated || wasCharacterTruncated;
|
|
31511
33141
|
if (wasCharacterTruncated) {
|
|
@@ -32281,6 +33911,40 @@ class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
32281
33911
|
}
|
|
32282
33912
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
32283
33913
|
|
|
33914
|
+
/**
|
|
33915
|
+
* Supported visibility states for persisted agents.
|
|
33916
|
+
*
|
|
33917
|
+
* @public exported from `@promptbook/core`
|
|
33918
|
+
*/
|
|
33919
|
+
const AGENT_VISIBILITY_VALUES = ['PRIVATE', 'UNLISTED', 'PUBLIC'];
|
|
33920
|
+
/**
|
|
33921
|
+
* Returns `true` when the value is one of supported visibility states.
|
|
33922
|
+
*
|
|
33923
|
+
* @param value - Raw value to validate.
|
|
33924
|
+
* @returns Whether the value is a valid `AgentVisibility`.
|
|
33925
|
+
*
|
|
33926
|
+
* @public exported from `@promptbook/core`
|
|
33927
|
+
*/
|
|
33928
|
+
function isAgentVisibility(value) {
|
|
33929
|
+
return typeof value === 'string' && AGENT_VISIBILITY_VALUES.includes(value);
|
|
33930
|
+
}
|
|
33931
|
+
/**
|
|
33932
|
+
* Normalizes raw visibility text into a supported value.
|
|
33933
|
+
*
|
|
33934
|
+
* @param value - Raw visibility value.
|
|
33935
|
+
* @returns Normalized visibility, or `null` when invalid.
|
|
33936
|
+
*
|
|
33937
|
+
* @public exported from `@promptbook/core`
|
|
33938
|
+
*/
|
|
33939
|
+
function normalizeAgentVisibility(value) {
|
|
33940
|
+
if (typeof value !== 'string') {
|
|
33941
|
+
return null;
|
|
33942
|
+
}
|
|
33943
|
+
const normalized = value.trim().toUpperCase();
|
|
33944
|
+
return isAgentVisibility(normalized) ? normalized : null;
|
|
33945
|
+
}
|
|
33946
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
33947
|
+
|
|
32284
33948
|
/**
|
|
32285
33949
|
* Maximum allowed source length for create-agent payloads.
|
|
32286
33950
|
*
|
|
@@ -34534,6 +36198,7 @@ const COMMITMENT_REGISTRY = [
|
|
|
34534
36198
|
new MetaDomainCommitmentDefinition(),
|
|
34535
36199
|
new MetaDisclaimerCommitmentDefinition(),
|
|
34536
36200
|
new MetaInputPlaceholderCommitmentDefinition(),
|
|
36201
|
+
new MetaVisibilityCommitmentDefinition(),
|
|
34537
36202
|
new MetaCommitmentDefinition(),
|
|
34538
36203
|
new MetaVoiceCommitmentDefinition(),
|
|
34539
36204
|
new NoteCommitmentDefinition('NOTE'),
|
|
@@ -34994,11 +36659,19 @@ const JsonFileImportPlugin = {
|
|
|
34994
36659
|
try {
|
|
34995
36660
|
const json = JSON.parse(content);
|
|
34996
36661
|
const formattedJson = JSON.stringify(json, null, 4);
|
|
34997
|
-
return
|
|
36662
|
+
return spaceTrim$1((block) => `
|
|
36663
|
+
\`\`\`json
|
|
36664
|
+
${block(formattedJson)}
|
|
36665
|
+
\`\`\`
|
|
36666
|
+
`);
|
|
34998
36667
|
}
|
|
34999
36668
|
catch (error) {
|
|
35000
36669
|
// If JSON is invalid, still import it but maybe not as pretty JSON
|
|
35001
|
-
return
|
|
36670
|
+
return spaceTrim$1((block) => `
|
|
36671
|
+
\`\`\`json
|
|
36672
|
+
${block(content)}
|
|
36673
|
+
\`\`\`
|
|
36674
|
+
`);
|
|
35002
36675
|
}
|
|
35003
36676
|
},
|
|
35004
36677
|
};
|
|
@@ -35022,7 +36695,11 @@ const TextFileImportPlugin = {
|
|
|
35022
36695
|
import(content, mimeType) {
|
|
35023
36696
|
const extension = mimeTypeToExtension(mimeType);
|
|
35024
36697
|
const codeBlockType = extension || 'txt';
|
|
35025
|
-
return
|
|
36698
|
+
return spaceTrim$1((block) => `
|
|
36699
|
+
\`\`\`${codeBlockType}
|
|
36700
|
+
${block(content)}
|
|
36701
|
+
\`\`\`
|
|
36702
|
+
`);
|
|
35026
36703
|
},
|
|
35027
36704
|
};
|
|
35028
36705
|
|
|
@@ -35269,11 +36946,19 @@ function collectExampleInteractionLines(parseResult, samples) {
|
|
|
35269
36946
|
const examples = [];
|
|
35270
36947
|
const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
|
|
35271
36948
|
if (initialMessage) {
|
|
35272
|
-
examples.push(
|
|
36949
|
+
examples.push(spaceTrim$1((block) => `
|
|
36950
|
+
**Agent:**
|
|
36951
|
+
${block(initialMessage)}
|
|
36952
|
+
`));
|
|
35273
36953
|
}
|
|
35274
36954
|
if (samples && samples.length > 0) {
|
|
35275
36955
|
for (const sample of samples) {
|
|
35276
|
-
examples.push(
|
|
36956
|
+
examples.push(spaceTrim$1((block) => `
|
|
36957
|
+
**User:** ${block(String(sample.question))}
|
|
36958
|
+
|
|
36959
|
+
**Agent:**
|
|
36960
|
+
${block(sample.answer)}
|
|
36961
|
+
`));
|
|
35277
36962
|
}
|
|
35278
36963
|
}
|
|
35279
36964
|
return examples;
|
|
@@ -35867,6 +37552,7 @@ const META_COMMITMENT_APPLIERS = {
|
|
|
35867
37552
|
'META COLOR': applyMetaColorContent,
|
|
35868
37553
|
'META FONT': applyMetaFontContent,
|
|
35869
37554
|
'META VOICE': applyMetaVoiceContent,
|
|
37555
|
+
'META VISIBILITY': applyMetaVisibilityContent,
|
|
35870
37556
|
};
|
|
35871
37557
|
/**
|
|
35872
37558
|
* Applies META-style commitments that mutate parsed profile metadata.
|
|
@@ -35896,6 +37582,10 @@ function applyGenericMetaCommitment(state, content) {
|
|
|
35896
37582
|
applyMetaAvatarContent(state, metaValue);
|
|
35897
37583
|
return;
|
|
35898
37584
|
}
|
|
37585
|
+
if (metaTypeRaw.toUpperCase() === 'VISIBILITY') {
|
|
37586
|
+
applyMetaVisibilityContent(state, metaValue);
|
|
37587
|
+
return;
|
|
37588
|
+
}
|
|
35899
37589
|
const metaType = normalizeTo_camelCase(metaTypeRaw);
|
|
35900
37590
|
state.meta[metaType] = metaValue;
|
|
35901
37591
|
}
|
|
@@ -35972,6 +37662,15 @@ function applyMetaFontContent(state, content) {
|
|
|
35972
37662
|
function applyMetaVoiceContent(state, content) {
|
|
35973
37663
|
state.meta.voice = spaceTrim$1(content);
|
|
35974
37664
|
}
|
|
37665
|
+
/**
|
|
37666
|
+
* Applies META VISIBILITY content into the normalized `meta.visibility` field.
|
|
37667
|
+
*/
|
|
37668
|
+
function applyMetaVisibilityContent(state, content) {
|
|
37669
|
+
const visibility = normalizeAgentVisibility(content);
|
|
37670
|
+
if (visibility) {
|
|
37671
|
+
state.meta.visibility = visibility;
|
|
37672
|
+
}
|
|
37673
|
+
}
|
|
35975
37674
|
/**
|
|
35976
37675
|
* Normalizes the separator in the content
|
|
35977
37676
|
*
|
|
@@ -39798,7 +41497,12 @@ function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFull
|
|
|
39798
41497
|
* @private internal function of `$registeredLlmToolsMessage`
|
|
39799
41498
|
*/
|
|
39800
41499
|
function createUsedEnvMessage() {
|
|
39801
|
-
return $usedEnvFilename === null
|
|
41500
|
+
return $usedEnvFilename === null
|
|
41501
|
+
? `Unknown \`.env\` file`
|
|
41502
|
+
: spaceTrim$1(`
|
|
41503
|
+
Used \`.env\` file:
|
|
41504
|
+
${$usedEnvFilename}
|
|
41505
|
+
`);
|
|
39802
41506
|
}
|
|
39803
41507
|
// TODO: [®] DRY Register logic
|
|
39804
41508
|
// TODO: [🧠][⚛] Maybe pass env as argument
|
|
@@ -40338,7 +42042,7 @@ function cacheLlmTools(llmTools, options = {}) {
|
|
|
40338
42042
|
*
|
|
40339
42043
|
* It looks for environment variables:
|
|
40340
42044
|
* - `process.env.OPENAI_API_KEY`
|
|
40341
|
-
* - `process.env.ANTHROPIC_CLAUDE_API_KEY`
|
|
42045
|
+
* - `process.env.ANTHROPIC_API_KEY` (or the deprecated `process.env.ANTHROPIC_CLAUDE_API_KEY`)
|
|
40342
42046
|
* - ...
|
|
40343
42047
|
*
|
|
40344
42048
|
* @see Environment variables documentation or .env file for required variables.
|
|
@@ -40438,7 +42142,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
|
|
|
40438
42142
|
*
|
|
40439
42143
|
* It looks for environment variables:
|
|
40440
42144
|
* - `process.env.OPENAI_API_KEY`
|
|
40441
|
-
* - `process.env.ANTHROPIC_CLAUDE_API_KEY`
|
|
42145
|
+
* - `process.env.ANTHROPIC_API_KEY` (or the deprecated `process.env.ANTHROPIC_CLAUDE_API_KEY`)
|
|
40442
42146
|
* - ...
|
|
40443
42147
|
*
|
|
40444
42148
|
* @param options Configuration options for the LLM tools
|
|
@@ -48868,7 +50572,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
|
|
|
48868
50572
|
if (!executionTools || !executionTools.script) {
|
|
48869
50573
|
throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
|
|
48870
50574
|
}
|
|
48871
|
-
return Array.isArray(executionTools.script)
|
|
50575
|
+
return Array.isArray(executionTools.script)
|
|
50576
|
+
? executionTools.script
|
|
50577
|
+
: [executionTools.script];
|
|
48872
50578
|
}
|
|
48873
50579
|
/**
|
|
48874
50580
|
* Resolves the assistant-visible AgentKit tool response while preserving structured tool result data.
|
|
@@ -49290,7 +50996,7 @@ function emitAgentLlmExecutionToolsAssistantPreparationProgress(options) {
|
|
|
49290
50996
|
* Computes one stable hash from a JSON-serializable value.
|
|
49291
50997
|
*/
|
|
49292
50998
|
function computeJsonHash$1(value) {
|
|
49293
|
-
return SHA256(JSON.stringify(value)).toString();
|
|
50999
|
+
return CryptoJS.SHA256(JSON.stringify(value)).toString();
|
|
49294
51000
|
}
|
|
49295
51001
|
/**
|
|
49296
51002
|
* Handles OpenAI AgentKit-backed executions for `AgentLlmExecutionTools`.
|
|
@@ -49448,7 +51154,7 @@ AgentLlmExecutionToolsAgentKitRunner.vectorStoreCache = new Map();
|
|
|
49448
51154
|
* Computes one stable hash from a JSON-serializable value.
|
|
49449
51155
|
*/
|
|
49450
51156
|
function computeJsonHash(value) {
|
|
49451
|
-
return SHA256(JSON.stringify(value)).toString();
|
|
51157
|
+
return CryptoJS.SHA256(JSON.stringify(value)).toString();
|
|
49452
51158
|
}
|
|
49453
51159
|
/**
|
|
49454
51160
|
* Removes assistant-managed requirements before the prompt is executed via OpenAI Assistants.
|
|
@@ -49784,7 +51490,11 @@ function truncateAttachmentInlineText(content, maxCharacters) {
|
|
|
49784
51490
|
};
|
|
49785
51491
|
}
|
|
49786
51492
|
return {
|
|
49787
|
-
content:
|
|
51493
|
+
content: spaceTrim$1((block) => `
|
|
51494
|
+
${block(content.slice(0, Math.max(0, maxCharacters)))}
|
|
51495
|
+
|
|
51496
|
+
[...truncated...]
|
|
51497
|
+
`),
|
|
49788
51498
|
isTruncated: true,
|
|
49789
51499
|
};
|
|
49790
51500
|
}
|
|
@@ -50250,7 +51960,7 @@ class AgentLlmExecutionTools {
|
|
|
50250
51960
|
* Returns a virtual model name representing the agent behavior.
|
|
50251
51961
|
*/
|
|
50252
51962
|
get modelName() {
|
|
50253
|
-
const hash = SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
|
|
51963
|
+
const hash = CryptoJS.SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
|
|
50254
51964
|
const agentId = hash.substring(0, 10);
|
|
50255
51965
|
return (normalizeToKebabCase(this.title) + '-' + agentId);
|
|
50256
51966
|
}
|