@promptbook/wizard 0.112.0-99 → 0.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -21
- package/esm/index.es.js +1700 -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 +0 -2
- package/esm/src/_packages/node.index.d.ts +40 -0
- package/esm/src/_packages/types.index.d.ts +18 -0
- package/esm/src/avatars/avatarAnimationScheduler.d.ts +4 -0
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/esm/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
- package/esm/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
- package/esm/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
- package/esm/src/book-3.0/CliAgent.d.ts +66 -0
- package/esm/src/book-3.0/CliAgent.test.d.ts +1 -0
- package/esm/src/book-3.0/LiteAgent.d.ts +68 -0
- package/esm/src/book-3.0/LiteAgent.test.d.ts +1 -0
- package/esm/src/book-3.0/agentFolderPaths.d.ts +30 -0
- package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
- package/esm/src/book-components/BookEditor/BookEditor.d.ts +6 -5
- package/esm/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
- package/esm/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
- package/esm/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
- package/esm/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +1 -0
- package/esm/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
- package/esm/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
- package/esm/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
- package/esm/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
- package/esm/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
- package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
- package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/esm/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
- package/esm/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
- package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
- package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
- package/esm/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/esm/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
- package/esm/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
- package/esm/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
- package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
- package/esm/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
- package/esm/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
- package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
- package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
- package/esm/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +10 -0
- package/esm/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
- package/esm/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
- package/esm/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
- package/esm/src/book-components/_common/Modal/Modal.d.ts +1 -1
- package/esm/src/book-components/icons/AboutIcon.d.ts +1 -1
- package/esm/src/book-components/icons/DownloadIcon.d.ts +1 -1
- package/esm/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
- package/esm/src/book-components/icons/FullscreenIcon.d.ts +1 -1
- package/esm/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
- package/esm/src/cli/cli-commands/agent/chat.d.ts +10 -0
- package/esm/src/cli/cli-commands/agent/exec.d.ts +10 -0
- package/esm/src/cli/cli-commands/agent/run.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -24
- package/esm/src/cli/cli-commands/agent.d.ts +14 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +17 -1
- package/esm/src/cli/cli-commands/agents-server/run.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +9 -2
- package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.d.ts +25 -0
- package/esm/src/cli/cli-commands/coder/find-unwritten.d.ts +10 -0
- package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +2 -0
- package/esm/src/cli/cli-commands/coder/server.d.ts +13 -0
- package/esm/src/cli/cli-commands/coder/waitOptions.d.ts +14 -0
- package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -25
- package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
- package/esm/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
- package/esm/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
- package/esm/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
- package/esm/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
- package/esm/src/utils/isTimingSafeEqualString.d.ts +25 -0
- package/esm/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +1701 -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 +0 -2
- package/umd/src/_packages/node.index.d.ts +40 -0
- package/umd/src/_packages/types.index.d.ts +18 -0
- package/umd/src/avatars/avatarAnimationScheduler.d.ts +4 -0
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/umd/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
- package/umd/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
- package/umd/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
- package/umd/src/book-3.0/CliAgent.d.ts +66 -0
- package/umd/src/book-3.0/CliAgent.test.d.ts +1 -0
- package/umd/src/book-3.0/LiteAgent.d.ts +68 -0
- package/umd/src/book-3.0/LiteAgent.test.d.ts +1 -0
- package/umd/src/book-3.0/agentFolderPaths.d.ts +30 -0
- package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
- package/umd/src/book-components/BookEditor/BookEditor.d.ts +6 -5
- package/umd/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
- package/umd/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
- package/umd/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
- package/umd/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +1 -0
- package/umd/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
- package/umd/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
- package/umd/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
- package/umd/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
- package/umd/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
- package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
- package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/umd/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
- package/umd/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
- package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
- package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
- package/umd/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/umd/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
- package/umd/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
- package/umd/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
- package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
- package/umd/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
- package/umd/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
- package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
- package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
- package/umd/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +10 -0
- package/umd/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
- package/umd/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
- package/umd/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
- package/umd/src/book-components/_common/Modal/Modal.d.ts +1 -1
- package/umd/src/book-components/icons/AboutIcon.d.ts +1 -1
- package/umd/src/book-components/icons/DownloadIcon.d.ts +1 -1
- package/umd/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
- package/umd/src/book-components/icons/FullscreenIcon.d.ts +1 -1
- package/umd/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
- package/umd/src/cli/cli-commands/agent/chat.d.ts +10 -0
- package/umd/src/cli/cli-commands/agent/exec.d.ts +10 -0
- package/umd/src/cli/cli-commands/agent/run.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -24
- package/umd/src/cli/cli-commands/agent.d.ts +14 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +17 -1
- package/umd/src/cli/cli-commands/agents-server/run.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +9 -2
- package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
- package/umd/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.d.ts +25 -0
- package/umd/src/cli/cli-commands/coder/find-unwritten.d.ts +10 -0
- package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +2 -0
- package/umd/src/cli/cli-commands/coder/server.d.ts +13 -0
- package/umd/src/cli/cli-commands/coder/waitOptions.d.ts +14 -0
- package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -25
- package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
- package/umd/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
- package/umd/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
- package/umd/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
- package/umd/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
- package/umd/src/utils/isTimingSafeEqualString.d.ts +25 -0
- package/umd/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
- package/umd/src/version.d.ts +1 -1
package/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.112.0
|
|
41
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.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,
|
|
@@ -27679,7 +29233,15 @@ function buildTeammateMetadata(entry) {
|
|
|
27679
29233
|
* Builds the teammate request text, optionally including context.
|
|
27680
29234
|
*/
|
|
27681
29235
|
function buildTeammateRequest(message, context) {
|
|
27682
|
-
|
|
29236
|
+
if (!context) {
|
|
29237
|
+
return message;
|
|
29238
|
+
}
|
|
29239
|
+
return spaceTrim$1((block) => `
|
|
29240
|
+
${block(message)}
|
|
29241
|
+
|
|
29242
|
+
Context:
|
|
29243
|
+
${block(context)}
|
|
29244
|
+
`);
|
|
27683
29245
|
}
|
|
27684
29246
|
/**
|
|
27685
29247
|
* Builds a minimal chat prompt for teammate calls.
|
|
@@ -31505,7 +33067,11 @@ function createUseProjectToolFunctions() {
|
|
|
31505
33067
|
const lineRangedContent = applyOptionalLineRange(decodedContent, args.startLine, args.endLine);
|
|
31506
33068
|
const wasCharacterTruncated = lineRangedContent.length > MAX_PROJECT_FILE_CONTENT_CHARACTERS;
|
|
31507
33069
|
const contentToReturn = wasCharacterTruncated
|
|
31508
|
-
?
|
|
33070
|
+
? spaceTrim$1((block) => `
|
|
33071
|
+
${block(lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS))}
|
|
33072
|
+
|
|
33073
|
+
[...truncated...]
|
|
33074
|
+
`)
|
|
31509
33075
|
: lineRangedContent;
|
|
31510
33076
|
const wasTruncated = decoded.isTruncated || wasCharacterTruncated;
|
|
31511
33077
|
if (wasCharacterTruncated) {
|
|
@@ -34994,11 +36560,19 @@ const JsonFileImportPlugin = {
|
|
|
34994
36560
|
try {
|
|
34995
36561
|
const json = JSON.parse(content);
|
|
34996
36562
|
const formattedJson = JSON.stringify(json, null, 4);
|
|
34997
|
-
return
|
|
36563
|
+
return spaceTrim$1((block) => `
|
|
36564
|
+
\`\`\`json
|
|
36565
|
+
${block(formattedJson)}
|
|
36566
|
+
\`\`\`
|
|
36567
|
+
`);
|
|
34998
36568
|
}
|
|
34999
36569
|
catch (error) {
|
|
35000
36570
|
// If JSON is invalid, still import it but maybe not as pretty JSON
|
|
35001
|
-
return
|
|
36571
|
+
return spaceTrim$1((block) => `
|
|
36572
|
+
\`\`\`json
|
|
36573
|
+
${block(content)}
|
|
36574
|
+
\`\`\`
|
|
36575
|
+
`);
|
|
35002
36576
|
}
|
|
35003
36577
|
},
|
|
35004
36578
|
};
|
|
@@ -35022,7 +36596,11 @@ const TextFileImportPlugin = {
|
|
|
35022
36596
|
import(content, mimeType) {
|
|
35023
36597
|
const extension = mimeTypeToExtension(mimeType);
|
|
35024
36598
|
const codeBlockType = extension || 'txt';
|
|
35025
|
-
return
|
|
36599
|
+
return spaceTrim$1((block) => `
|
|
36600
|
+
\`\`\`${codeBlockType}
|
|
36601
|
+
${block(content)}
|
|
36602
|
+
\`\`\`
|
|
36603
|
+
`);
|
|
35026
36604
|
},
|
|
35027
36605
|
};
|
|
35028
36606
|
|
|
@@ -35269,11 +36847,19 @@ function collectExampleInteractionLines(parseResult, samples) {
|
|
|
35269
36847
|
const examples = [];
|
|
35270
36848
|
const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
|
|
35271
36849
|
if (initialMessage) {
|
|
35272
|
-
examples.push(
|
|
36850
|
+
examples.push(spaceTrim$1((block) => `
|
|
36851
|
+
**Agent:**
|
|
36852
|
+
${block(initialMessage)}
|
|
36853
|
+
`));
|
|
35273
36854
|
}
|
|
35274
36855
|
if (samples && samples.length > 0) {
|
|
35275
36856
|
for (const sample of samples) {
|
|
35276
|
-
examples.push(
|
|
36857
|
+
examples.push(spaceTrim$1((block) => `
|
|
36858
|
+
**User:** ${block(String(sample.question))}
|
|
36859
|
+
|
|
36860
|
+
**Agent:**
|
|
36861
|
+
${block(sample.answer)}
|
|
36862
|
+
`));
|
|
35277
36863
|
}
|
|
35278
36864
|
}
|
|
35279
36865
|
return examples;
|
|
@@ -39798,7 +41384,12 @@ function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFull
|
|
|
39798
41384
|
* @private internal function of `$registeredLlmToolsMessage`
|
|
39799
41385
|
*/
|
|
39800
41386
|
function createUsedEnvMessage() {
|
|
39801
|
-
return $usedEnvFilename === null
|
|
41387
|
+
return $usedEnvFilename === null
|
|
41388
|
+
? `Unknown \`.env\` file`
|
|
41389
|
+
: spaceTrim$1(`
|
|
41390
|
+
Used \`.env\` file:
|
|
41391
|
+
${$usedEnvFilename}
|
|
41392
|
+
`);
|
|
39802
41393
|
}
|
|
39803
41394
|
// TODO: [®] DRY Register logic
|
|
39804
41395
|
// TODO: [🧠][⚛] Maybe pass env as argument
|
|
@@ -40338,7 +41929,7 @@ function cacheLlmTools(llmTools, options = {}) {
|
|
|
40338
41929
|
*
|
|
40339
41930
|
* It looks for environment variables:
|
|
40340
41931
|
* - `process.env.OPENAI_API_KEY`
|
|
40341
|
-
* - `process.env.ANTHROPIC_CLAUDE_API_KEY`
|
|
41932
|
+
* - `process.env.ANTHROPIC_API_KEY` (or the deprecated `process.env.ANTHROPIC_CLAUDE_API_KEY`)
|
|
40342
41933
|
* - ...
|
|
40343
41934
|
*
|
|
40344
41935
|
* @see Environment variables documentation or .env file for required variables.
|
|
@@ -40438,7 +42029,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
|
|
|
40438
42029
|
*
|
|
40439
42030
|
* It looks for environment variables:
|
|
40440
42031
|
* - `process.env.OPENAI_API_KEY`
|
|
40441
|
-
* - `process.env.ANTHROPIC_CLAUDE_API_KEY`
|
|
42032
|
+
* - `process.env.ANTHROPIC_API_KEY` (or the deprecated `process.env.ANTHROPIC_CLAUDE_API_KEY`)
|
|
40442
42033
|
* - ...
|
|
40443
42034
|
*
|
|
40444
42035
|
* @param options Configuration options for the LLM tools
|
|
@@ -48868,7 +50459,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
|
|
|
48868
50459
|
if (!executionTools || !executionTools.script) {
|
|
48869
50460
|
throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
|
|
48870
50461
|
}
|
|
48871
|
-
return Array.isArray(executionTools.script)
|
|
50462
|
+
return Array.isArray(executionTools.script)
|
|
50463
|
+
? executionTools.script
|
|
50464
|
+
: [executionTools.script];
|
|
48872
50465
|
}
|
|
48873
50466
|
/**
|
|
48874
50467
|
* Resolves the assistant-visible AgentKit tool response while preserving structured tool result data.
|
|
@@ -49290,7 +50883,7 @@ function emitAgentLlmExecutionToolsAssistantPreparationProgress(options) {
|
|
|
49290
50883
|
* Computes one stable hash from a JSON-serializable value.
|
|
49291
50884
|
*/
|
|
49292
50885
|
function computeJsonHash$1(value) {
|
|
49293
|
-
return SHA256(JSON.stringify(value)).toString();
|
|
50886
|
+
return CryptoJS.SHA256(JSON.stringify(value)).toString();
|
|
49294
50887
|
}
|
|
49295
50888
|
/**
|
|
49296
50889
|
* Handles OpenAI AgentKit-backed executions for `AgentLlmExecutionTools`.
|
|
@@ -49448,7 +51041,7 @@ AgentLlmExecutionToolsAgentKitRunner.vectorStoreCache = new Map();
|
|
|
49448
51041
|
* Computes one stable hash from a JSON-serializable value.
|
|
49449
51042
|
*/
|
|
49450
51043
|
function computeJsonHash(value) {
|
|
49451
|
-
return SHA256(JSON.stringify(value)).toString();
|
|
51044
|
+
return CryptoJS.SHA256(JSON.stringify(value)).toString();
|
|
49452
51045
|
}
|
|
49453
51046
|
/**
|
|
49454
51047
|
* Removes assistant-managed requirements before the prompt is executed via OpenAI Assistants.
|
|
@@ -49784,7 +51377,11 @@ function truncateAttachmentInlineText(content, maxCharacters) {
|
|
|
49784
51377
|
};
|
|
49785
51378
|
}
|
|
49786
51379
|
return {
|
|
49787
|
-
content:
|
|
51380
|
+
content: spaceTrim$1((block) => `
|
|
51381
|
+
${block(content.slice(0, Math.max(0, maxCharacters)))}
|
|
51382
|
+
|
|
51383
|
+
[...truncated...]
|
|
51384
|
+
`),
|
|
49788
51385
|
isTruncated: true,
|
|
49789
51386
|
};
|
|
49790
51387
|
}
|
|
@@ -50250,7 +51847,7 @@ class AgentLlmExecutionTools {
|
|
|
50250
51847
|
* Returns a virtual model name representing the agent behavior.
|
|
50251
51848
|
*/
|
|
50252
51849
|
get modelName() {
|
|
50253
|
-
const hash = SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
|
|
51850
|
+
const hash = CryptoJS.SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
|
|
50254
51851
|
const agentId = hash.substring(0, 10);
|
|
50255
51852
|
return (normalizeToKebabCase(this.title) + '-' + agentId);
|
|
50256
51853
|
}
|