@promptbook/core 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 +1767 -128
- package/esm/index.es.js.map +1 -1
- package/esm/servers.d.ts +1 -9
- 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 +1 -1
- package/umd/index.umd.js +1769 -129
- package/umd/index.umd.js.map +1 -1
- package/umd/servers.d.ts +1 -9
- 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
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import CryptoJS from 'crypto-js';
|
|
2
2
|
import hexEncoder from 'crypto-js/enc-hex';
|
|
3
3
|
import { spaceTrim as spaceTrim$1 } from 'spacetrim';
|
|
4
|
-
import { randomBytes } from 'crypto';
|
|
5
4
|
import { Subject, BehaviorSubject } from 'rxjs';
|
|
6
5
|
import { forTime } from 'waitasecond';
|
|
7
6
|
import sha256 from 'crypto-js/sha256';
|
|
8
7
|
import { basename, join, dirname, isAbsolute } from 'path';
|
|
9
8
|
import { lookup, extension } from 'mime-types';
|
|
10
|
-
import
|
|
9
|
+
import papaparse from 'papaparse';
|
|
11
10
|
import moment from 'moment';
|
|
11
|
+
import { randomBytes } from 'crypto';
|
|
12
12
|
import colors from 'colors';
|
|
13
13
|
import { fileSearchTool, tool, Agent as Agent$1, webSearchTool, run, setDefaultOpenAIClient, setDefaultOpenAIKey } from '@openai/agents';
|
|
14
14
|
import Bottleneck from 'bottleneck';
|
|
@@ -28,7 +28,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
28
28
|
* @generated
|
|
29
29
|
* @see https://github.com/webgptorg/promptbook
|
|
30
30
|
*/
|
|
31
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0
|
|
31
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0';
|
|
32
32
|
/**
|
|
33
33
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
34
34
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -1653,7 +1653,7 @@ function valueToString(value) {
|
|
|
1653
1653
|
* @public exported from `@promptbook/utils`
|
|
1654
1654
|
*/
|
|
1655
1655
|
function computeHash(value) {
|
|
1656
|
-
return SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
|
|
1656
|
+
return CryptoJS.SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
|
|
1657
1657
|
}
|
|
1658
1658
|
// TODO: [🥬][🥬] Use this ACRY
|
|
1659
1659
|
|
|
@@ -3773,7 +3773,7 @@ class NotYetImplementedError extends Error {
|
|
|
3773
3773
|
* @private internal helper function
|
|
3774
3774
|
*/
|
|
3775
3775
|
function $randomToken(randomness) {
|
|
3776
|
-
return
|
|
3776
|
+
return CryptoJS.lib.WordArray.random(randomness).toString(CryptoJS.enc.Hex);
|
|
3777
3777
|
}
|
|
3778
3778
|
// TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
|
|
3779
3779
|
// TODO: Maybe use nanoid instead https://github.com/ai/nanoid
|
|
@@ -5457,7 +5457,7 @@ function normalizeToKebabCase(text) {
|
|
|
5457
5457
|
* @public exported from `@promptbook/editable`
|
|
5458
5458
|
*/
|
|
5459
5459
|
function knowledgeSourceContentToName(knowledgeSourceContent) {
|
|
5460
|
-
const hash = SHA256(hexEncoder.parse(JSON.stringify(knowledgeSourceContent)))
|
|
5460
|
+
const hash = CryptoJS.SHA256(hexEncoder.parse(JSON.stringify(knowledgeSourceContent)))
|
|
5461
5461
|
// <- TODO: [🥬] Encapsulate sha256 to some private utility function
|
|
5462
5462
|
.toString( /* hex */)
|
|
5463
5463
|
.substring(0, 20);
|
|
@@ -6370,7 +6370,7 @@ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO:
|
|
|
6370
6370
|
console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
|
|
6371
6371
|
value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
6372
6372
|
}
|
|
6373
|
-
const csv = parse(value, settings);
|
|
6373
|
+
const csv = papaparse.parse(value, settings);
|
|
6374
6374
|
return csv;
|
|
6375
6375
|
}
|
|
6376
6376
|
|
|
@@ -6455,10 +6455,10 @@ const CsvFormatParser = {
|
|
|
6455
6455
|
i > index ? { ...row, [outputParameterName]: PENDING_VALUE_PLACEHOLDER } : row,
|
|
6456
6456
|
);
|
|
6457
6457
|
*/
|
|
6458
|
-
await onProgress(unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
|
|
6458
|
+
await onProgress(papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
|
|
6459
6459
|
}
|
|
6460
6460
|
}
|
|
6461
|
-
return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
6461
|
+
return papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
6462
6462
|
},
|
|
6463
6463
|
},
|
|
6464
6464
|
{
|
|
@@ -6486,7 +6486,7 @@ const CsvFormatParser = {
|
|
|
6486
6486
|
return /* not await */ mapCallback({ [key]: value }, index, array.length);
|
|
6487
6487
|
}));
|
|
6488
6488
|
}));
|
|
6489
|
-
return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
6489
|
+
return papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
6490
6490
|
},
|
|
6491
6491
|
},
|
|
6492
6492
|
],
|
|
@@ -9898,6 +9898,11 @@ function isVoidPseudoAgentReference(rawReference) {
|
|
|
9898
9898
|
* - `isValidAgentUrl` *(this one)* which tests just agent URL
|
|
9899
9899
|
* - `isValidPipelineUrl` which tests just pipeline URL
|
|
9900
9900
|
*
|
|
9901
|
+
* Note: This is a pure structural validator and does not block private/internal network
|
|
9902
|
+
* addresses. Callers that fetch the URL server-side from an untrusted network context must
|
|
9903
|
+
* additionally apply the `assertSafeUrl` SSRF guard from the Agents Server before any
|
|
9904
|
+
* outbound request.
|
|
9905
|
+
*
|
|
9901
9906
|
* @public exported from `@promptbook/utils`
|
|
9902
9907
|
*/
|
|
9903
9908
|
function isValidAgentUrl(url) {
|
|
@@ -9911,12 +9916,6 @@ function isValidAgentUrl(url) {
|
|
|
9911
9916
|
// TODO: [🐠]
|
|
9912
9917
|
return false;
|
|
9913
9918
|
}
|
|
9914
|
-
/*
|
|
9915
|
-
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
9916
|
-
if (isUrlOnPrivateNetwork(url)) {
|
|
9917
|
-
return false;
|
|
9918
|
-
}
|
|
9919
|
-
*/
|
|
9920
9919
|
return true;
|
|
9921
9920
|
}
|
|
9922
9921
|
// TODO: [🐠] Maybe more info why the URL is invalid
|
|
@@ -10092,8 +10091,11 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
10092
10091
|
if (!trimmedContent) {
|
|
10093
10092
|
return requirements;
|
|
10094
10093
|
}
|
|
10095
|
-
|
|
10096
|
-
|
|
10094
|
+
const goalSection = spaceTrim$1((block) => `
|
|
10095
|
+
## Goal
|
|
10096
|
+
|
|
10097
|
+
${block(trimmedContent)}
|
|
10098
|
+
`);
|
|
10097
10099
|
const requirementsWithGoal = this.appendToSystemMessage(requirements, goalSection, '\n\n');
|
|
10098
10100
|
return this.appendToPromptSuffix(requirementsWithGoal, trimmedContent);
|
|
10099
10101
|
}
|
|
@@ -10615,8 +10617,11 @@ class LanguageCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
10615
10617
|
if (!trimmedContent) {
|
|
10616
10618
|
return requirements;
|
|
10617
10619
|
}
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
+
const languageSection = spaceTrim$1((block) => `
|
|
10621
|
+
## Language
|
|
10622
|
+
|
|
10623
|
+
- Your language is ${block(trimmedContent)}
|
|
10624
|
+
`);
|
|
10620
10625
|
return this.appendToSystemMessage(requirements, languageSection, '\n\n');
|
|
10621
10626
|
}
|
|
10622
10627
|
}
|
|
@@ -13063,21 +13068,22 @@ function getPointBounds(points) {
|
|
|
13063
13068
|
* @private helper of `fractalAvatarVisual`
|
|
13064
13069
|
*/
|
|
13065
13070
|
function drawDragonCurveLayer(context, points, options) {
|
|
13066
|
-
const {
|
|
13071
|
+
const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
|
|
13067
13072
|
const firstPoint = points[0];
|
|
13068
13073
|
const lastPoint = points[points.length - 1];
|
|
13069
13074
|
const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
|
|
13070
13075
|
ribbonGradient.addColorStop(0, `${primaryColor}f2`);
|
|
13071
13076
|
ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
|
|
13072
13077
|
ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
|
|
13078
|
+
// Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
|
|
13079
|
+
// context.filter blur, which triggers a costly software rasterization pass every frame.
|
|
13073
13080
|
context.save();
|
|
13074
13081
|
context.beginPath();
|
|
13075
13082
|
tracePolyline(context, points);
|
|
13076
|
-
context.strokeStyle = `${shadowColor}
|
|
13077
|
-
context.lineWidth = strokeWidth *
|
|
13083
|
+
context.strokeStyle = `${shadowColor}48`;
|
|
13084
|
+
context.lineWidth = strokeWidth * 4.5;
|
|
13078
13085
|
context.lineJoin = 'round';
|
|
13079
13086
|
context.lineCap = 'round';
|
|
13080
|
-
context.filter = `blur(${size * 0.022}px)`;
|
|
13081
13087
|
context.stroke();
|
|
13082
13088
|
context.restore();
|
|
13083
13089
|
context.beginPath();
|
|
@@ -13539,7 +13545,7 @@ function fillTextureRect(texture, x, y, width, height, color) {
|
|
|
13539
13545
|
*
|
|
13540
13546
|
* @private helper of `minecraft2AvatarVisual`
|
|
13541
13547
|
*/
|
|
13542
|
-
const LIGHT_DIRECTION$
|
|
13548
|
+
const LIGHT_DIRECTION$4 = normalizeVector3({
|
|
13543
13549
|
x: 0.4,
|
|
13544
13550
|
y: -0.65,
|
|
13545
13551
|
z: 0.92,
|
|
@@ -13661,11 +13667,23 @@ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY,
|
|
|
13661
13667
|
* @private helper of `minecraft2AvatarVisual`
|
|
13662
13668
|
*/
|
|
13663
13669
|
function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
|
|
13670
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.03;
|
|
13671
|
+
const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
|
|
13672
|
+
const rx = size * (0.16 + interaction.intensity * 0.015);
|
|
13673
|
+
const ry = size * 0.055;
|
|
13674
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
13664
13675
|
context.save();
|
|
13665
|
-
context.
|
|
13666
|
-
context.
|
|
13676
|
+
context.translate(cx, cy);
|
|
13677
|
+
context.scale(1, ry / rx);
|
|
13678
|
+
const blurRadius = rx * 1.4;
|
|
13679
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
13680
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
13681
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
13682
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
13683
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
13684
|
+
context.fillStyle = shadowGradient;
|
|
13667
13685
|
context.beginPath();
|
|
13668
|
-
context.
|
|
13686
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
13669
13687
|
context.fill();
|
|
13670
13688
|
context.restore();
|
|
13671
13689
|
}
|
|
@@ -13751,7 +13769,7 @@ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
|
|
|
13751
13769
|
corners: projectedCorners,
|
|
13752
13770
|
texture: faceDefinition.texture,
|
|
13753
13771
|
averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
|
|
13754
|
-
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$
|
|
13772
|
+
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$4), -1, 1),
|
|
13755
13773
|
outlineColor: cuboid.outlineColor,
|
|
13756
13774
|
};
|
|
13757
13775
|
});
|
|
@@ -13889,13 +13907,27 @@ const minecraftAvatarVisual = {
|
|
|
13889
13907
|
spotlight.addColorStop(1, `${palette.highlight}00`);
|
|
13890
13908
|
context.fillStyle = spotlight;
|
|
13891
13909
|
context.fillRect(0, 0, size, size);
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13910
|
+
{
|
|
13911
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
13912
|
+
const cx = size * 0.5;
|
|
13913
|
+
const cy = size * 0.86;
|
|
13914
|
+
const rx = size * 0.2;
|
|
13915
|
+
const ry = size * 0.06;
|
|
13916
|
+
const blurRadius = rx * 1.4;
|
|
13917
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
13918
|
+
shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
|
|
13919
|
+
shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
|
|
13920
|
+
shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
|
|
13921
|
+
shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
|
|
13922
|
+
context.save();
|
|
13923
|
+
context.translate(cx, cy);
|
|
13924
|
+
context.scale(1, ry / rx);
|
|
13925
|
+
context.fillStyle = shadowGradient;
|
|
13926
|
+
context.beginPath();
|
|
13927
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
13928
|
+
context.fill();
|
|
13929
|
+
context.restore();
|
|
13930
|
+
}
|
|
13899
13931
|
drawVoxelCuboid(context, {
|
|
13900
13932
|
x: bodyX,
|
|
13901
13933
|
y: bodyY,
|
|
@@ -14424,7 +14456,7 @@ function resolveSeededIntegerRange(random, minimumValue, maximumValue) {
|
|
|
14424
14456
|
*
|
|
14425
14457
|
* @private helper of `octopus3AvatarVisual`
|
|
14426
14458
|
*/
|
|
14427
|
-
function formatAlphaHex$
|
|
14459
|
+
function formatAlphaHex$2(opacity) {
|
|
14428
14460
|
return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
|
|
14429
14461
|
.toString(16)
|
|
14430
14462
|
.padStart(2, '0');
|
|
@@ -14803,7 +14835,7 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
|
|
|
14803
14835
|
context.beginPath();
|
|
14804
14836
|
context.moveTo(-radiusX * 0.74, radiusY * 0.2);
|
|
14805
14837
|
context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
|
|
14806
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex$
|
|
14838
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$2(eyeStyle.lowerLidOpacity)}`;
|
|
14807
14839
|
context.lineWidth = radiusX * 0.08;
|
|
14808
14840
|
context.lineCap = 'round';
|
|
14809
14841
|
context.stroke();
|
|
@@ -14884,7 +14916,7 @@ function drawProjectedOrganicEye(context, localCenter, radiusX, radiusY, center,
|
|
|
14884
14916
|
context.beginPath();
|
|
14885
14917
|
context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
14886
14918
|
context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
14887
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
|
|
14919
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
|
|
14888
14920
|
context.lineWidth = projectedRadiusX * 0.08;
|
|
14889
14921
|
context.lineCap = 'round';
|
|
14890
14922
|
context.stroke();
|
|
@@ -14930,7 +14962,7 @@ function drawProjectedQuad(context, corners, fillStyle) {
|
|
|
14930
14962
|
*
|
|
14931
14963
|
* @private helper of the 3D octopus avatar visuals
|
|
14932
14964
|
*/
|
|
14933
|
-
function formatAlphaHex(opacity) {
|
|
14965
|
+
function formatAlphaHex$1(opacity) {
|
|
14934
14966
|
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
14935
14967
|
.toString(16)
|
|
14936
14968
|
.padStart(2, '0');
|
|
@@ -14942,11 +14974,40 @@ function formatAlphaHex(opacity) {
|
|
|
14942
14974
|
*
|
|
14943
14975
|
* @private helper of `octopus3dAvatarVisual`
|
|
14944
14976
|
*/
|
|
14945
|
-
const LIGHT_DIRECTION$
|
|
14977
|
+
const LIGHT_DIRECTION$3 = normalizeVector3({
|
|
14946
14978
|
x: 0.48,
|
|
14947
14979
|
y: -0.62,
|
|
14948
14980
|
z: 0.94,
|
|
14949
14981
|
});
|
|
14982
|
+
/**
|
|
14983
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
14984
|
+
*
|
|
14985
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
14986
|
+
*/
|
|
14987
|
+
const octopus3dStableStateCache = new WeakMap();
|
|
14988
|
+
/**
|
|
14989
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
14990
|
+
*
|
|
14991
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
14992
|
+
*/
|
|
14993
|
+
function getOctopus3dStableState(createRandom) {
|
|
14994
|
+
const cached = octopus3dStableStateCache.get(createRandom);
|
|
14995
|
+
if (cached !== undefined) {
|
|
14996
|
+
return cached;
|
|
14997
|
+
}
|
|
14998
|
+
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
14999
|
+
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
15000
|
+
const leftEyePhaseOffset = eyeRandom() * 0.6;
|
|
15001
|
+
const rightEyePhaseOffset = eyeRandom() * 0.6;
|
|
15002
|
+
const state = {
|
|
15003
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
15004
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
15005
|
+
leftEyePhaseOffset,
|
|
15006
|
+
rightEyePhaseOffset,
|
|
15007
|
+
};
|
|
15008
|
+
octopus3dStableStateCache.set(createRandom, state);
|
|
15009
|
+
return state;
|
|
15010
|
+
}
|
|
14950
15011
|
/**
|
|
14951
15012
|
* Proper 3D Octopus visual built from projected organic meshes and tentacles.
|
|
14952
15013
|
*
|
|
@@ -14959,10 +15020,7 @@ const octopus3dAvatarVisual = {
|
|
|
14959
15020
|
isAnimated: true,
|
|
14960
15021
|
supportsPointerTracking: true,
|
|
14961
15022
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
14962
|
-
const morphologyProfile =
|
|
14963
|
-
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
14964
|
-
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
14965
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
15023
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
|
|
14966
15024
|
const sceneCenterX = size * 0.5;
|
|
14967
15025
|
const sceneCenterY = size * 0.56;
|
|
14968
15026
|
const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
|
|
@@ -15059,12 +15117,12 @@ const octopus3dAvatarVisual = {
|
|
|
15059
15117
|
x: -faceEyeSpacing,
|
|
15060
15118
|
y: faceEyeYOffset,
|
|
15061
15119
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
|
|
15062
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
15120
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
15063
15121
|
drawProjectedOrganicEye(context, {
|
|
15064
15122
|
x: faceEyeSpacing,
|
|
15065
15123
|
y: faceEyeYOffset,
|
|
15066
15124
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
|
|
15067
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 +
|
|
15125
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
15068
15126
|
drawProjectedOrganicMouth(context, [
|
|
15069
15127
|
{
|
|
15070
15128
|
x: -mouthHalfWidth,
|
|
@@ -15108,14 +15166,28 @@ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCent
|
|
|
15108
15166
|
/**
|
|
15109
15167
|
* Draws the soft ground shadow below the octopus.
|
|
15110
15168
|
*
|
|
15169
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
15170
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
15171
|
+
*
|
|
15111
15172
|
* @private helper of `octopus3dAvatarVisual`
|
|
15112
15173
|
*/
|
|
15113
15174
|
function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
|
|
15175
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.04;
|
|
15176
|
+
const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
|
|
15177
|
+
const rx = size * (0.18 + interaction.intensity * 0.02);
|
|
15178
|
+
const ry = size * 0.06;
|
|
15114
15179
|
context.save();
|
|
15115
|
-
context.
|
|
15116
|
-
context.
|
|
15180
|
+
context.translate(cx, cy);
|
|
15181
|
+
context.scale(1, ry / rx);
|
|
15182
|
+
const blurRadius = rx * 1.4;
|
|
15183
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
15184
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
15185
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
15186
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
15187
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
15188
|
+
context.fillStyle = shadowGradient;
|
|
15117
15189
|
context.beginPath();
|
|
15118
|
-
context.
|
|
15190
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
15119
15191
|
context.fill();
|
|
15120
15192
|
context.restore();
|
|
15121
15193
|
}
|
|
@@ -15152,7 +15224,7 @@ function resolveVisibleEllipsoidPatches(options) {
|
|
|
15152
15224
|
corners: projectedCorners,
|
|
15153
15225
|
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
15154
15226
|
transformedCorners.length,
|
|
15155
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$
|
|
15227
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
|
|
15156
15228
|
fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
|
|
15157
15229
|
outlineColor,
|
|
15158
15230
|
});
|
|
@@ -15341,11 +15413,40 @@ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
|
|
|
15341
15413
|
*
|
|
15342
15414
|
* @private helper of `octopus3d2AvatarVisual`
|
|
15343
15415
|
*/
|
|
15344
|
-
const LIGHT_DIRECTION = normalizeVector3({
|
|
15416
|
+
const LIGHT_DIRECTION$2 = normalizeVector3({
|
|
15345
15417
|
x: 0.38,
|
|
15346
15418
|
y: -0.6,
|
|
15347
15419
|
z: 0.98,
|
|
15348
15420
|
});
|
|
15421
|
+
/**
|
|
15422
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
15423
|
+
*
|
|
15424
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
15425
|
+
*/
|
|
15426
|
+
const octopus3d2StableStateCache = new WeakMap();
|
|
15427
|
+
/**
|
|
15428
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
15429
|
+
*
|
|
15430
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
15431
|
+
*/
|
|
15432
|
+
function getOctopus3d2StableState(createRandom) {
|
|
15433
|
+
const cached = octopus3d2StableStateCache.get(createRandom);
|
|
15434
|
+
if (cached !== undefined) {
|
|
15435
|
+
return cached;
|
|
15436
|
+
}
|
|
15437
|
+
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
15438
|
+
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
15439
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
15440
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
15441
|
+
const state = {
|
|
15442
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
15443
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
15444
|
+
leftEyePhaseOffset,
|
|
15445
|
+
rightEyePhaseOffset,
|
|
15446
|
+
};
|
|
15447
|
+
octopus3d2StableStateCache.set(createRandom, state);
|
|
15448
|
+
return state;
|
|
15449
|
+
}
|
|
15349
15450
|
/**
|
|
15350
15451
|
* Octopus 3D 2 avatar visual.
|
|
15351
15452
|
*
|
|
@@ -15358,10 +15459,7 @@ const octopus3d2AvatarVisual = {
|
|
|
15358
15459
|
isAnimated: true,
|
|
15359
15460
|
supportsPointerTracking: true,
|
|
15360
15461
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
15361
|
-
const morphologyProfile =
|
|
15362
|
-
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
15363
|
-
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
15364
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
15462
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
|
|
15365
15463
|
const sceneCenterX = size * 0.5;
|
|
15366
15464
|
const sceneCenterY = size * 0.575;
|
|
15367
15465
|
const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
|
|
@@ -15414,8 +15512,8 @@ const octopus3d2AvatarVisual = {
|
|
|
15414
15512
|
const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
|
|
15415
15513
|
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
15416
15514
|
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
15417
|
-
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
15418
|
-
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 +
|
|
15515
|
+
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
15516
|
+
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
15419
15517
|
drawProjectedOrganicMouth(context, [
|
|
15420
15518
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
15421
15519
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
@@ -15444,54 +15542,118 @@ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, scene
|
|
|
15444
15542
|
/**
|
|
15445
15543
|
* Draws the soft floor shadow that anchors the single mesh in the frame.
|
|
15446
15544
|
*
|
|
15545
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
15546
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
15547
|
+
*
|
|
15447
15548
|
* @private helper of `octopus3d2AvatarVisual`
|
|
15448
15549
|
*/
|
|
15449
15550
|
function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
15551
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
15552
|
+
const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
|
|
15553
|
+
const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
|
|
15554
|
+
const ry = size * 0.062;
|
|
15450
15555
|
context.save();
|
|
15451
|
-
context.
|
|
15452
|
-
context.
|
|
15556
|
+
context.translate(cx, cy);
|
|
15557
|
+
context.scale(1, ry / rx);
|
|
15558
|
+
const blurRadius = rx * 1.4;
|
|
15559
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
15560
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
15561
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
15562
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
15563
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
15564
|
+
context.fillStyle = shadowGradient;
|
|
15453
15565
|
context.beginPath();
|
|
15454
|
-
context.
|
|
15566
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
15455
15567
|
context.fill();
|
|
15456
15568
|
context.restore();
|
|
15457
15569
|
}
|
|
15570
|
+
/**
|
|
15571
|
+
* Number of latitude segments used by the single blobby octopus mesh.
|
|
15572
|
+
*
|
|
15573
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
15574
|
+
*/
|
|
15575
|
+
const LATITUDE_PATCH_COUNT$2 = 12;
|
|
15576
|
+
/**
|
|
15577
|
+
* Number of longitude segments used by the single blobby octopus mesh.
|
|
15578
|
+
*
|
|
15579
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
15580
|
+
*/
|
|
15581
|
+
const LONGITUDE_PATCH_COUNT$2 = 24;
|
|
15458
15582
|
/**
|
|
15459
15583
|
* Resolves all visible projected patches for the single blobby octopus mesh.
|
|
15460
15584
|
*
|
|
15585
|
+
* Within a single frame, mesh corner samples and longitude-only lobe-wave values are
|
|
15586
|
+
* quantized to the patch grid and computed once each rather than re-evaluated for every
|
|
15587
|
+
* patch corner — the patch loop alone would call `sampleBlobbyOctopusSurfacePoint`
|
|
15588
|
+
* `latitudePatchCount * longitudePatchCount * 4` times without caching, even though most
|
|
15589
|
+
* corners are shared between neighboring patches.
|
|
15590
|
+
*
|
|
15461
15591
|
* @private helper of `octopus3d2AvatarVisual`
|
|
15462
15592
|
*/
|
|
15463
15593
|
function resolveVisibleBlobbyOctopusPatches(options) {
|
|
15464
15594
|
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
|
|
15465
|
-
const latitudePatchCount =
|
|
15466
|
-
const longitudePatchCount =
|
|
15595
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT$2;
|
|
15596
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT$2;
|
|
15467
15597
|
const surfacePatches = [];
|
|
15598
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
15599
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
15600
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
15601
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
15602
|
+
}
|
|
15603
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
15604
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
15605
|
+
}
|
|
15606
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
15607
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
15608
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveLowerLobeWave(longitudeBoundaries[boundaryIndex], morphologyProfile, animationPhase, timeMs);
|
|
15609
|
+
}
|
|
15610
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
15611
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
15612
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
15613
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
15614
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
15615
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
15616
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
15617
|
+
const cornerSample = sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
15618
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
15619
|
+
}
|
|
15620
|
+
}
|
|
15468
15621
|
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
15469
|
-
const startLatitude =
|
|
15470
|
-
const endLatitude =
|
|
15622
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
15623
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
15471
15624
|
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
15472
15625
|
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
15626
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
15627
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
15473
15628
|
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
15474
|
-
const startLongitude =
|
|
15475
|
-
const endLongitude =
|
|
15629
|
+
const startLongitude = longitudeBoundaries[longitudeIndex];
|
|
15630
|
+
const endLongitude = longitudeBoundaries[longitudeIndex + 1];
|
|
15476
15631
|
const centerLongitude = (startLongitude + endLongitude) / 2;
|
|
15477
|
-
const
|
|
15478
|
-
|
|
15479
|
-
|
|
15480
|
-
|
|
15481
|
-
|
|
15632
|
+
const transformedCorners = [
|
|
15633
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
15634
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
15635
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
15636
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
15482
15637
|
];
|
|
15483
|
-
const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
|
|
15484
15638
|
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
15485
15639
|
if (surfaceNormal.z <= 0.01) {
|
|
15486
15640
|
continue;
|
|
15487
15641
|
}
|
|
15488
|
-
const projectedCorners =
|
|
15642
|
+
const projectedCorners = [
|
|
15643
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
15644
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
15645
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
15646
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
15647
|
+
];
|
|
15489
15648
|
surfacePatches.push({
|
|
15490
15649
|
corners: projectedCorners,
|
|
15491
|
-
averageDepth: transformedCorners.
|
|
15492
|
-
transformedCorners.
|
|
15493
|
-
|
|
15494
|
-
|
|
15650
|
+
averageDepth: (transformedCorners[0].z +
|
|
15651
|
+
transformedCorners[1].z +
|
|
15652
|
+
transformedCorners[2].z +
|
|
15653
|
+
transformedCorners[3].z) /
|
|
15654
|
+
4,
|
|
15655
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
|
|
15656
|
+
fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
|
|
15495
15657
|
outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
|
|
15496
15658
|
});
|
|
15497
15659
|
}
|
|
@@ -15507,12 +15669,21 @@ function resolveVisibleBlobbyOctopusPatches(options) {
|
|
|
15507
15669
|
* @private helper of `octopus3d2AvatarVisual`
|
|
15508
15670
|
*/
|
|
15509
15671
|
function sampleBlobbyOctopusSurfacePoint(options, latitude, longitude) {
|
|
15672
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
15673
|
+
return sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs));
|
|
15674
|
+
}
|
|
15675
|
+
/**
|
|
15676
|
+
* Samples one point on the continuous Octopus 3D 2 surface using a precomputed lower-lobe wave
|
|
15677
|
+
* to skip the per-call trig evaluation for `latitudePatchCount + 1` longitude-shared corners.
|
|
15678
|
+
*
|
|
15679
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
15680
|
+
*/
|
|
15681
|
+
function sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, lowerLobeWave) {
|
|
15510
15682
|
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
15511
15683
|
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
15512
15684
|
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
15513
15685
|
const upperBlend = Math.pow(1 - verticalProgress, 1.2);
|
|
15514
15686
|
const lowerBlend = Math.pow(verticalProgress, 1.42);
|
|
15515
|
-
const lowerLobeWave = resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs);
|
|
15516
15687
|
const skirtEnvelope = Math.pow(cosineLatitude, 0.5) * lowerBlend;
|
|
15517
15688
|
const horizontalScale = 1.02 +
|
|
15518
15689
|
skirtEnvelope * (0.34 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.22 + lowerLobeWave * 0.22) -
|
|
@@ -15550,7 +15721,7 @@ function resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, time
|
|
|
15550
15721
|
*
|
|
15551
15722
|
* @private helper of `octopus3d2AvatarVisual`
|
|
15552
15723
|
*/
|
|
15553
|
-
function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, lowerLobeWave) {
|
|
15724
|
+
function resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, forwardness, lowerLobeWave) {
|
|
15554
15725
|
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.12 - forwardness * 0.07, 0, 1);
|
|
15555
15726
|
if (tonalProgress < 0.16) {
|
|
15556
15727
|
return palette.highlight;
|
|
@@ -15590,6 +15761,1384 @@ function drawBlobbySurfacePatch(context, surfacePatch) {
|
|
|
15590
15761
|
context.restore();
|
|
15591
15762
|
}
|
|
15592
15763
|
|
|
15764
|
+
/* eslint-disable no-magic-numbers */
|
|
15765
|
+
/**
|
|
15766
|
+
* Light direction used by the continuous octopus mesh shading.
|
|
15767
|
+
*
|
|
15768
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15769
|
+
*/
|
|
15770
|
+
const LIGHT_DIRECTION$1 = normalizeVector3({
|
|
15771
|
+
x: 0.34,
|
|
15772
|
+
y: -0.62,
|
|
15773
|
+
z: 1,
|
|
15774
|
+
});
|
|
15775
|
+
/**
|
|
15776
|
+
* Real-octopus tentacle count used by the continuous lower mesh.
|
|
15777
|
+
*
|
|
15778
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15779
|
+
*/
|
|
15780
|
+
const OCTOPUS_TENTACLE_COUNT$1 = 8;
|
|
15781
|
+
/**
|
|
15782
|
+
* Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
|
|
15783
|
+
* mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
|
|
15784
|
+
* React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
|
|
15785
|
+
*
|
|
15786
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15787
|
+
*/
|
|
15788
|
+
const stableStateCache = new WeakMap();
|
|
15789
|
+
/**
|
|
15790
|
+
* Returns the stable per-avatar state, computing it on first access and returning the cached
|
|
15791
|
+
* result on every subsequent call within the same `<Avatar/>` mount.
|
|
15792
|
+
*
|
|
15793
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15794
|
+
*/
|
|
15795
|
+
function getOctopus3d3StableState(createRandom) {
|
|
15796
|
+
const cached = stableStateCache.get(createRandom);
|
|
15797
|
+
if (cached !== undefined) {
|
|
15798
|
+
return cached;
|
|
15799
|
+
}
|
|
15800
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
15801
|
+
const animationRandom = createRandom('octopus3d3-animation-profile');
|
|
15802
|
+
const eyeRandom = createRandom('octopus3d3-eye-profile');
|
|
15803
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
15804
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
15805
|
+
const state = {
|
|
15806
|
+
morphologyProfile,
|
|
15807
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
15808
|
+
leftEyePhaseOffset,
|
|
15809
|
+
rightEyePhaseOffset,
|
|
15810
|
+
tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
15811
|
+
};
|
|
15812
|
+
stableStateCache.set(createRandom, state);
|
|
15813
|
+
return state;
|
|
15814
|
+
}
|
|
15815
|
+
/**
|
|
15816
|
+
* Octopus 3D 3 avatar visual.
|
|
15817
|
+
*
|
|
15818
|
+
* @private built-in avatar visual
|
|
15819
|
+
*/
|
|
15820
|
+
const octopus3d3AvatarVisual = {
|
|
15821
|
+
id: 'octopus3d3',
|
|
15822
|
+
title: 'Octopus 3D 3',
|
|
15823
|
+
description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
|
|
15824
|
+
isAnimated: true,
|
|
15825
|
+
supportsPointerTracking: true,
|
|
15826
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
15827
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
|
|
15828
|
+
const sceneCenterX = size * 0.5;
|
|
15829
|
+
const sceneCenterY = size * 0.535;
|
|
15830
|
+
const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
|
|
15831
|
+
const meshCenter = {
|
|
15832
|
+
x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
|
|
15833
|
+
y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
|
|
15834
|
+
z: interaction.intensity * size * 0.018,
|
|
15835
|
+
};
|
|
15836
|
+
const rotationY = -0.1 +
|
|
15837
|
+
Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
|
|
15838
|
+
interaction.bodyOffsetX * 0.22 +
|
|
15839
|
+
interaction.gazeX * 0.88;
|
|
15840
|
+
const rotationX = -0.07 +
|
|
15841
|
+
Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
|
|
15842
|
+
interaction.bodyOffsetY * 0.08 -
|
|
15843
|
+
interaction.gazeY * 0.38;
|
|
15844
|
+
const surfaceOptions = {
|
|
15845
|
+
radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
|
|
15846
|
+
radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
|
|
15847
|
+
radiusZ: size *
|
|
15848
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
15849
|
+
(1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
|
|
15850
|
+
morphologyProfile,
|
|
15851
|
+
timeMs,
|
|
15852
|
+
animationPhase,
|
|
15853
|
+
tentacleProfiles,
|
|
15854
|
+
};
|
|
15855
|
+
const surfacePatches = resolveVisibleContinuousOctopusPatches({
|
|
15856
|
+
...surfaceOptions,
|
|
15857
|
+
center: meshCenter,
|
|
15858
|
+
rotationX,
|
|
15859
|
+
rotationY,
|
|
15860
|
+
sceneCenterX,
|
|
15861
|
+
sceneCenterY,
|
|
15862
|
+
size,
|
|
15863
|
+
palette,
|
|
15864
|
+
});
|
|
15865
|
+
const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
|
|
15866
|
+
const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
|
|
15867
|
+
const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
|
|
15868
|
+
const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
|
|
15869
|
+
const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
|
|
15870
|
+
const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
|
|
15871
|
+
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
|
|
15872
|
+
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
|
|
15873
|
+
drawAvatarFrame(context, size, palette);
|
|
15874
|
+
drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
15875
|
+
drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
|
|
15876
|
+
for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
15877
|
+
drawContinuousSurfacePatch(context, surfacePatch);
|
|
15878
|
+
}
|
|
15879
|
+
drawProjectedSurfaceCurrents({
|
|
15880
|
+
context,
|
|
15881
|
+
surfaceOptions,
|
|
15882
|
+
center: meshCenter,
|
|
15883
|
+
rotationX,
|
|
15884
|
+
rotationY,
|
|
15885
|
+
sceneCenterX,
|
|
15886
|
+
sceneCenterY,
|
|
15887
|
+
size,
|
|
15888
|
+
palette,
|
|
15889
|
+
morphologyProfile,
|
|
15890
|
+
timeMs,
|
|
15891
|
+
animationPhase,
|
|
15892
|
+
});
|
|
15893
|
+
drawProjectedTentacleSuckers({
|
|
15894
|
+
context,
|
|
15895
|
+
surfaceOptions,
|
|
15896
|
+
center: meshCenter,
|
|
15897
|
+
rotationX,
|
|
15898
|
+
rotationY,
|
|
15899
|
+
sceneCenterX,
|
|
15900
|
+
sceneCenterY,
|
|
15901
|
+
size,
|
|
15902
|
+
palette,
|
|
15903
|
+
});
|
|
15904
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
15905
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
15906
|
+
drawProjectedOrganicMouth(context, [
|
|
15907
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
15908
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
15909
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
|
|
15910
|
+
], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
|
|
15911
|
+
},
|
|
15912
|
+
};
|
|
15913
|
+
/**
|
|
15914
|
+
* Creates seeded tentacle-lobe profiles around the visible lower octopus body.
|
|
15915
|
+
*
|
|
15916
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15917
|
+
*/
|
|
15918
|
+
function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
15919
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT$1 }, (_, tentacleIndex) => {
|
|
15920
|
+
const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
|
|
15921
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT$1 - 1);
|
|
15922
|
+
return {
|
|
15923
|
+
centerLongitude: -Math.PI * 0.86 +
|
|
15924
|
+
progress * Math.PI * 1.72 +
|
|
15925
|
+
(tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
|
|
15926
|
+
widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
|
|
15927
|
+
lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
|
|
15928
|
+
swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
|
|
15929
|
+
depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
|
|
15930
|
+
phase: tentacleRandom() * Math.PI * 2,
|
|
15931
|
+
suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
|
|
15932
|
+
};
|
|
15933
|
+
});
|
|
15934
|
+
}
|
|
15935
|
+
/**
|
|
15936
|
+
* Draws the soft underwater atmosphere behind the continuous octopus mesh.
|
|
15937
|
+
*
|
|
15938
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15939
|
+
*/
|
|
15940
|
+
function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
15941
|
+
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));
|
|
15942
|
+
glowGradient.addColorStop(0, `${palette.highlight}66`);
|
|
15943
|
+
glowGradient.addColorStop(0.34, `${palette.accent}2e`);
|
|
15944
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
15945
|
+
context.fillStyle = glowGradient;
|
|
15946
|
+
context.fillRect(0, 0, size, size);
|
|
15947
|
+
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);
|
|
15948
|
+
lowerGradient.addColorStop(0, `${palette.secondary}25`);
|
|
15949
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
15950
|
+
context.fillStyle = lowerGradient;
|
|
15951
|
+
context.fillRect(0, 0, size, size);
|
|
15952
|
+
}
|
|
15953
|
+
/**
|
|
15954
|
+
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
15955
|
+
*
|
|
15956
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
15957
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
15958
|
+
*
|
|
15959
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15960
|
+
*/
|
|
15961
|
+
function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
15962
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
15963
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
15964
|
+
const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
|
|
15965
|
+
const ry = size * 0.06;
|
|
15966
|
+
// Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
|
|
15967
|
+
// then fill with a radial gradient that approximates the blurry edge without context.filter.
|
|
15968
|
+
context.save();
|
|
15969
|
+
context.translate(cx, cy);
|
|
15970
|
+
context.scale(1, ry / rx);
|
|
15971
|
+
const blurRadius = rx * 1.4;
|
|
15972
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
15973
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
15974
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
15975
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
15976
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
15977
|
+
context.fillStyle = shadowGradient;
|
|
15978
|
+
context.beginPath();
|
|
15979
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
15980
|
+
context.fill();
|
|
15981
|
+
context.restore();
|
|
15982
|
+
}
|
|
15983
|
+
/**
|
|
15984
|
+
* Number of latitude segments used by the continuous Octopus 3D 3 mesh.
|
|
15985
|
+
*
|
|
15986
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15987
|
+
*/
|
|
15988
|
+
const LATITUDE_PATCH_COUNT$1 = 16;
|
|
15989
|
+
/**
|
|
15990
|
+
* Number of longitude segments used by the continuous Octopus 3D 3 mesh.
|
|
15991
|
+
*
|
|
15992
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
15993
|
+
*/
|
|
15994
|
+
const LONGITUDE_PATCH_COUNT$1 = 40;
|
|
15995
|
+
/**
|
|
15996
|
+
* Resolves visible projected patches for the continuous octopus mesh.
|
|
15997
|
+
*
|
|
15998
|
+
* Within a single frame, mesh corner samples and longitude-only computations (tentacle
|
|
15999
|
+
* influence and lobe wave) are quantized to the patch grid and computed once each rather
|
|
16000
|
+
* than re-evaluated for every patch corner — the patch loop alone calls `sampleContinuousOctopusSurfacePoint`
|
|
16001
|
+
* for `latitudePatchCount * longitudePatchCount * 4` corners without caching, and each call
|
|
16002
|
+
* triggers an inner 8-iteration `Math.exp` loop, which dominates the per-frame cost.
|
|
16003
|
+
*
|
|
16004
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16005
|
+
*/
|
|
16006
|
+
function resolveVisibleContinuousOctopusPatches(options) {
|
|
16007
|
+
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
16008
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT$1;
|
|
16009
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT$1;
|
|
16010
|
+
const surfacePatches = [];
|
|
16011
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
16012
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
16013
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
16014
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
16015
|
+
}
|
|
16016
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
16017
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
16018
|
+
}
|
|
16019
|
+
const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
|
|
16020
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
16021
|
+
const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
|
|
16022
|
+
const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
16023
|
+
const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
16024
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
16025
|
+
const cornerLongitude = longitudeBoundaries[boundaryIndex];
|
|
16026
|
+
cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveContinuousTentacleInfluence(options, cornerLongitude);
|
|
16027
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveContinuousLobeWave(options, cornerLongitude);
|
|
16028
|
+
}
|
|
16029
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
16030
|
+
const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
|
|
16031
|
+
cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveContinuousTentacleInfluence(options, patchCenterLongitude);
|
|
16032
|
+
cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveContinuousLobeWave(options, patchCenterLongitude);
|
|
16033
|
+
cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
|
|
16034
|
+
}
|
|
16035
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
16036
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
16037
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
16038
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
16039
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
16040
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
16041
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
16042
|
+
const cornerSample = sampleContinuousOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
16043
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
16044
|
+
}
|
|
16045
|
+
}
|
|
16046
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
16047
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
16048
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
16049
|
+
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
16050
|
+
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
16051
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
16052
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
16053
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
16054
|
+
const transformedCorners = [
|
|
16055
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
16056
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
16057
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
16058
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
16059
|
+
];
|
|
16060
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
16061
|
+
if (surfaceNormal.z <= 0.008) {
|
|
16062
|
+
continue;
|
|
16063
|
+
}
|
|
16064
|
+
const projectedCorners = [
|
|
16065
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
16066
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
16067
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
16068
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
16069
|
+
];
|
|
16070
|
+
surfacePatches.push({
|
|
16071
|
+
corners: projectedCorners,
|
|
16072
|
+
averageDepth: (transformedCorners[0].z +
|
|
16073
|
+
transformedCorners[1].z +
|
|
16074
|
+
transformedCorners[2].z +
|
|
16075
|
+
transformedCorners[3].z) /
|
|
16076
|
+
4,
|
|
16077
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
|
|
16078
|
+
fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
16079
|
+
outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
|
|
16080
|
+
});
|
|
16081
|
+
}
|
|
16082
|
+
}
|
|
16083
|
+
return surfacePatches;
|
|
16084
|
+
}
|
|
16085
|
+
/**
|
|
16086
|
+
* Samples one point on the continuous Octopus 3D 3 surface.
|
|
16087
|
+
*
|
|
16088
|
+
* The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
|
|
16089
|
+
* tentacled while still being rendered as one connected blobby mesh.
|
|
16090
|
+
*
|
|
16091
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16092
|
+
*/
|
|
16093
|
+
function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
|
|
16094
|
+
return sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveContinuousTentacleInfluence(options, longitude), resolveContinuousLobeWave(options, longitude));
|
|
16095
|
+
}
|
|
16096
|
+
/**
|
|
16097
|
+
* Samples one point on the continuous Octopus 3D 3 surface using precomputed longitude-only
|
|
16098
|
+
* values to skip the per-call `Math.exp` tentacle-influence loop and the lobe-wave trig call.
|
|
16099
|
+
*
|
|
16100
|
+
* The patch loop quantizes the mesh into a fixed `(latitudePatchCount + 1) * (longitudePatchCount + 1)`
|
|
16101
|
+
* corner grid, so the same longitude is reused across every latitude row and each
|
|
16102
|
+
* tentacle/lobe value can be computed once per frame instead of `latitudePatchCount * 4` times.
|
|
16103
|
+
*
|
|
16104
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16105
|
+
*/
|
|
16106
|
+
function sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
|
|
16107
|
+
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
16108
|
+
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
16109
|
+
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
16110
|
+
const upperBlend = Math.pow(1 - verticalProgress, 1.28);
|
|
16111
|
+
const lowerBlend = smoothStep$1(0.38, 1, verticalProgress);
|
|
16112
|
+
const tipBlend = smoothStep$1(0.68, 1, verticalProgress);
|
|
16113
|
+
const centerPull = resolveSignedAngularDistance$1(longitude, tentacleInfluence.centerLongitude);
|
|
16114
|
+
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
|
|
16115
|
+
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
16116
|
+
animationPhase * 0.6 +
|
|
16117
|
+
timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
|
|
16118
|
+
(0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
|
|
16119
|
+
(0.3 + lowerBlend * 0.7);
|
|
16120
|
+
const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
|
|
16121
|
+
lowerBlend *
|
|
16122
|
+
tentacleInfluence.core *
|
|
16123
|
+
tentacleInfluence.swayScale;
|
|
16124
|
+
const horizontalScale = 1.04 +
|
|
16125
|
+
mantleRipple +
|
|
16126
|
+
lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
|
|
16127
|
+
lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
|
|
16128
|
+
upperBlend * 0.08;
|
|
16129
|
+
const depthScale = 1.06 +
|
|
16130
|
+
upperBlend * 0.16 +
|
|
16131
|
+
Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
|
|
16132
|
+
lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
|
|
16133
|
+
Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
|
|
16134
|
+
const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) * radiusX;
|
|
16135
|
+
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
16136
|
+
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
|
|
16137
|
+
const lowerDrop = lowerBlend *
|
|
16138
|
+
radiusY *
|
|
16139
|
+
(0.18 +
|
|
16140
|
+
tentacleInfluence.core *
|
|
16141
|
+
(0.38 +
|
|
16142
|
+
tentacleInfluence.lengthScale * 0.22 +
|
|
16143
|
+
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
|
|
16144
|
+
return {
|
|
16145
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX + tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
|
|
16146
|
+
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
|
|
16147
|
+
upperBlend * radiusY * 0.1 +
|
|
16148
|
+
lowerDrop +
|
|
16149
|
+
Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
|
|
16150
|
+
Math.cos(timeMs / 880 + tentacleInfluence.phase) *
|
|
16151
|
+
lowerBlend *
|
|
16152
|
+
tipBlend *
|
|
16153
|
+
tentacleInfluence.core *
|
|
16154
|
+
radiusY *
|
|
16155
|
+
0.034,
|
|
16156
|
+
z: Math.cos(effectiveLongitude) * planarRadiusZ +
|
|
16157
|
+
Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
|
|
16158
|
+
lowerBlend *
|
|
16159
|
+
tentacleInfluence.core *
|
|
16160
|
+
radiusZ *
|
|
16161
|
+
0.04,
|
|
16162
|
+
};
|
|
16163
|
+
}
|
|
16164
|
+
/**
|
|
16165
|
+
* Blends nearby seeded tentacle profiles at one mesh longitude.
|
|
16166
|
+
*
|
|
16167
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16168
|
+
*/
|
|
16169
|
+
function resolveContinuousTentacleInfluence(options, longitude) {
|
|
16170
|
+
let totalWeight = 0;
|
|
16171
|
+
let weightedSin = 0;
|
|
16172
|
+
let weightedCos = 0;
|
|
16173
|
+
let weightedWidthScale = 0;
|
|
16174
|
+
let weightedLengthScale = 0;
|
|
16175
|
+
let weightedSwayScale = 0;
|
|
16176
|
+
let weightedDepthScale = 0;
|
|
16177
|
+
let weightedPhase = 0;
|
|
16178
|
+
for (const tentacleProfile of options.tentacleProfiles) {
|
|
16179
|
+
const distance = Math.abs(resolveSignedAngularDistance$1(longitude, tentacleProfile.centerLongitude));
|
|
16180
|
+
const width = 0.2 * tentacleProfile.widthScale;
|
|
16181
|
+
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
16182
|
+
totalWeight += weight;
|
|
16183
|
+
weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
|
|
16184
|
+
weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
|
|
16185
|
+
weightedWidthScale += tentacleProfile.widthScale * weight;
|
|
16186
|
+
weightedLengthScale += tentacleProfile.lengthScale * weight;
|
|
16187
|
+
weightedSwayScale += tentacleProfile.swayScale * weight;
|
|
16188
|
+
weightedDepthScale += tentacleProfile.depthScale * weight;
|
|
16189
|
+
weightedPhase += tentacleProfile.phase * weight;
|
|
16190
|
+
}
|
|
16191
|
+
if (totalWeight < 0.0001) {
|
|
16192
|
+
return {
|
|
16193
|
+
core: 0,
|
|
16194
|
+
centerLongitude: longitude,
|
|
16195
|
+
widthScale: 1,
|
|
16196
|
+
lengthScale: 1,
|
|
16197
|
+
swayScale: 1,
|
|
16198
|
+
depthScale: 1,
|
|
16199
|
+
phase: 0,
|
|
16200
|
+
};
|
|
16201
|
+
}
|
|
16202
|
+
return {
|
|
16203
|
+
core: clampNumber$1(totalWeight, 0, 1),
|
|
16204
|
+
centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
|
|
16205
|
+
widthScale: weightedWidthScale / totalWeight,
|
|
16206
|
+
lengthScale: weightedLengthScale / totalWeight,
|
|
16207
|
+
swayScale: weightedSwayScale / totalWeight,
|
|
16208
|
+
depthScale: weightedDepthScale / totalWeight,
|
|
16209
|
+
phase: weightedPhase / totalWeight,
|
|
16210
|
+
};
|
|
16211
|
+
}
|
|
16212
|
+
/**
|
|
16213
|
+
* Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
|
|
16214
|
+
*
|
|
16215
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16216
|
+
*/
|
|
16217
|
+
function resolveContinuousLobeWave(options, longitude) {
|
|
16218
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
16219
|
+
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT$1 +
|
|
16220
|
+
animationPhase +
|
|
16221
|
+
timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
|
|
16222
|
+
1) /
|
|
16223
|
+
2);
|
|
16224
|
+
}
|
|
16225
|
+
/**
|
|
16226
|
+
* Resolves one base fill tone for a patch on the continuous octopus mesh.
|
|
16227
|
+
*
|
|
16228
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16229
|
+
*/
|
|
16230
|
+
function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
|
|
16231
|
+
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
|
|
16232
|
+
if (tonalProgress < 0.14) {
|
|
16233
|
+
return palette.highlight;
|
|
16234
|
+
}
|
|
16235
|
+
if (tonalProgress < 0.32) {
|
|
16236
|
+
return palette.secondary;
|
|
16237
|
+
}
|
|
16238
|
+
if (tonalProgress < 0.72) {
|
|
16239
|
+
return forwardness > 0.55 ? palette.secondary : palette.primary;
|
|
16240
|
+
}
|
|
16241
|
+
return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
|
|
16242
|
+
}
|
|
16243
|
+
/**
|
|
16244
|
+
* Draws one projected mesh patch with soft shading and a subtle edge.
|
|
16245
|
+
*
|
|
16246
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16247
|
+
*/
|
|
16248
|
+
function drawContinuousSurfacePatch(context, surfacePatch) {
|
|
16249
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
16250
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
16251
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
|
|
16252
|
+
}
|
|
16253
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
16254
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
16255
|
+
}
|
|
16256
|
+
context.save();
|
|
16257
|
+
context.beginPath();
|
|
16258
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
16259
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
16260
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
16261
|
+
}
|
|
16262
|
+
context.closePath();
|
|
16263
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
16264
|
+
context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
|
|
16265
|
+
context.lineJoin = 'round';
|
|
16266
|
+
context.stroke();
|
|
16267
|
+
context.restore();
|
|
16268
|
+
}
|
|
16269
|
+
/**
|
|
16270
|
+
* Draws projected mantle-current lines on the front of the mesh.
|
|
16271
|
+
*
|
|
16272
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16273
|
+
*/
|
|
16274
|
+
function drawProjectedSurfaceCurrents(options) {
|
|
16275
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
|
|
16276
|
+
const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
|
|
16277
|
+
const centerIndex = (currentCount - 1) / 2;
|
|
16278
|
+
context.save();
|
|
16279
|
+
context.lineCap = 'round';
|
|
16280
|
+
context.lineJoin = 'round';
|
|
16281
|
+
for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
|
|
16282
|
+
const baseLongitude = (currentIndex - centerIndex) * 0.15;
|
|
16283
|
+
const projectedPoints = [];
|
|
16284
|
+
for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
|
|
16285
|
+
const progress = sampleIndex / 7;
|
|
16286
|
+
const latitude = -0.46 + progress * 0.74;
|
|
16287
|
+
const longitude = baseLongitude + Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
|
|
16288
|
+
const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
|
|
16289
|
+
if (scenePoint.z > center.z - size * 0.016) {
|
|
16290
|
+
projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
16291
|
+
}
|
|
16292
|
+
}
|
|
16293
|
+
if (projectedPoints.length < 3) {
|
|
16294
|
+
continue;
|
|
16295
|
+
}
|
|
16296
|
+
context.beginPath();
|
|
16297
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
16298
|
+
for (const projectedPoint of projectedPoints.slice(1)) {
|
|
16299
|
+
context.lineTo(projectedPoint.x, projectedPoint.y);
|
|
16300
|
+
}
|
|
16301
|
+
context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
|
|
16302
|
+
context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
|
|
16303
|
+
context.stroke();
|
|
16304
|
+
}
|
|
16305
|
+
context.restore();
|
|
16306
|
+
}
|
|
16307
|
+
/**
|
|
16308
|
+
* Draws small projected sucker highlights on the waving lower mesh lobes.
|
|
16309
|
+
*
|
|
16310
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16311
|
+
*/
|
|
16312
|
+
function drawProjectedTentacleSuckers(options) {
|
|
16313
|
+
const { surfaceOptions, size } = options;
|
|
16314
|
+
const { timeMs } = surfaceOptions;
|
|
16315
|
+
for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
|
|
16316
|
+
if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
|
|
16317
|
+
continue;
|
|
16318
|
+
}
|
|
16319
|
+
for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
|
|
16320
|
+
const latitude = 0.52 + suckerIndex * 0.14;
|
|
16321
|
+
const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
|
|
16322
|
+
const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
|
|
16323
|
+
drawProjectedSurfaceSpot({
|
|
16324
|
+
...options,
|
|
16325
|
+
latitude,
|
|
16326
|
+
longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
|
|
16327
|
+
radiusScale: size * (0.0065 - suckerIndex * 0.0007),
|
|
16328
|
+
});
|
|
16329
|
+
}
|
|
16330
|
+
}
|
|
16331
|
+
}
|
|
16332
|
+
/**
|
|
16333
|
+
* Draws one tiny projected surface spot by sampling local mesh tangents.
|
|
16334
|
+
*
|
|
16335
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16336
|
+
*/
|
|
16337
|
+
function drawProjectedSurfaceSpot(options) {
|
|
16338
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
|
|
16339
|
+
const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
|
|
16340
|
+
const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
|
|
16341
|
+
const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
|
|
16342
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
16343
|
+
if (sceneCenterPoint.z <= center.z - size * 0.012) {
|
|
16344
|
+
return;
|
|
16345
|
+
}
|
|
16346
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
16347
|
+
const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
16348
|
+
const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
16349
|
+
const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
|
|
16350
|
+
radiusScale *
|
|
16351
|
+
0.74, size * 0.003, size * 0.018);
|
|
16352
|
+
const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
|
|
16353
|
+
radiusScale *
|
|
16354
|
+
0.52, size * 0.0024, size * 0.014);
|
|
16355
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
16356
|
+
context.save();
|
|
16357
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
16358
|
+
context.rotate(rotation);
|
|
16359
|
+
context.beginPath();
|
|
16360
|
+
context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
|
|
16361
|
+
context.fillStyle = `${palette.highlight}73`;
|
|
16362
|
+
context.fill();
|
|
16363
|
+
context.strokeStyle = `${palette.highlight}99`;
|
|
16364
|
+
context.lineWidth = Math.max(0.7, size * 0.0028);
|
|
16365
|
+
context.stroke();
|
|
16366
|
+
context.restore();
|
|
16367
|
+
}
|
|
16368
|
+
/**
|
|
16369
|
+
* Resolves a signed angular distance from the source longitude to the target longitude.
|
|
16370
|
+
*
|
|
16371
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16372
|
+
*/
|
|
16373
|
+
function resolveSignedAngularDistance$1(sourceLongitude, targetLongitude) {
|
|
16374
|
+
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
16375
|
+
}
|
|
16376
|
+
/**
|
|
16377
|
+
* Smoothly maps a value between two bounds into `[0, 1]`.
|
|
16378
|
+
*
|
|
16379
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
16380
|
+
*/
|
|
16381
|
+
function smoothStep$1(edgeStart, edgeEnd, value) {
|
|
16382
|
+
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
16383
|
+
return progress * progress * (3 - 2 * progress);
|
|
16384
|
+
}
|
|
16385
|
+
|
|
16386
|
+
/* eslint-disable no-magic-numbers */
|
|
16387
|
+
/**
|
|
16388
|
+
* Light direction used by the continuous Octopus 3D 4 mesh shading.
|
|
16389
|
+
*
|
|
16390
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16391
|
+
*/
|
|
16392
|
+
const LIGHT_DIRECTION = normalizeVector3({
|
|
16393
|
+
x: 0.32,
|
|
16394
|
+
y: -0.66,
|
|
16395
|
+
z: 1,
|
|
16396
|
+
});
|
|
16397
|
+
/**
|
|
16398
|
+
* Rim-light direction used to brighten the side of the silhouette.
|
|
16399
|
+
*
|
|
16400
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16401
|
+
*/
|
|
16402
|
+
const RIM_LIGHT_DIRECTION = normalizeVector3({
|
|
16403
|
+
x: -0.7,
|
|
16404
|
+
y: -0.18,
|
|
16405
|
+
z: 0.55,
|
|
16406
|
+
});
|
|
16407
|
+
/**
|
|
16408
|
+
* Real-octopus tentacle count used by the continuous lower mesh.
|
|
16409
|
+
*
|
|
16410
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16411
|
+
*/
|
|
16412
|
+
const OCTOPUS_TENTACLE_COUNT = 8;
|
|
16413
|
+
/**
|
|
16414
|
+
* Number of seeded skin spots painted across the octopus body.
|
|
16415
|
+
*
|
|
16416
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16417
|
+
*/
|
|
16418
|
+
const SKIN_SPOT_COUNT = 14;
|
|
16419
|
+
/**
|
|
16420
|
+
* Cache keyed by the `createRandom` factory reference, stable per mounted `<Avatar/>` component.
|
|
16421
|
+
*
|
|
16422
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16423
|
+
*/
|
|
16424
|
+
const octopus3d4StableStateCache = new WeakMap();
|
|
16425
|
+
/**
|
|
16426
|
+
* Returns the stable per-avatar state, computing it on first access and caching afterwards.
|
|
16427
|
+
*
|
|
16428
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16429
|
+
*/
|
|
16430
|
+
function getOctopus3d4StableState(createRandom) {
|
|
16431
|
+
const cached = octopus3d4StableStateCache.get(createRandom);
|
|
16432
|
+
if (cached !== undefined) {
|
|
16433
|
+
return cached;
|
|
16434
|
+
}
|
|
16435
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
16436
|
+
const animationRandom = createRandom('octopus3d4-animation-profile');
|
|
16437
|
+
const eyeRandom = createRandom('octopus3d4-eye-profile');
|
|
16438
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
16439
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
16440
|
+
const state = {
|
|
16441
|
+
morphologyProfile,
|
|
16442
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
16443
|
+
leftEyePhaseOffset,
|
|
16444
|
+
rightEyePhaseOffset,
|
|
16445
|
+
tentacleProfiles: createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
16446
|
+
skinSpots: createBlobbySkinSpots(createRandom),
|
|
16447
|
+
};
|
|
16448
|
+
octopus3d4StableStateCache.set(createRandom, state);
|
|
16449
|
+
return state;
|
|
16450
|
+
}
|
|
16451
|
+
/**
|
|
16452
|
+
* Octopus 3D 4 avatar visual.
|
|
16453
|
+
*
|
|
16454
|
+
* @private built-in avatar visual
|
|
16455
|
+
*/
|
|
16456
|
+
const octopus3d4AvatarVisual = {
|
|
16457
|
+
id: 'octopus3d4',
|
|
16458
|
+
title: 'Octopus 3D 4',
|
|
16459
|
+
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.',
|
|
16460
|
+
isAnimated: true,
|
|
16461
|
+
supportsPointerTracking: true,
|
|
16462
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
16463
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
|
|
16464
|
+
const sceneCenterX = size * 0.5;
|
|
16465
|
+
const sceneCenterY = size * 0.535;
|
|
16466
|
+
const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
|
|
16467
|
+
const meshCenter = {
|
|
16468
|
+
x: interaction.bodyOffsetX * size * 0.052 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
|
|
16469
|
+
y: -size * 0.07 + interaction.bodyOffsetY * size * 0.028 + bob,
|
|
16470
|
+
z: interaction.intensity * size * 0.02,
|
|
16471
|
+
};
|
|
16472
|
+
const rotationY = -0.08 +
|
|
16473
|
+
Math.sin(timeMs / 2800 + animationPhase) * 0.04 +
|
|
16474
|
+
interaction.bodyOffsetX * 0.24 +
|
|
16475
|
+
interaction.gazeX * 0.98;
|
|
16476
|
+
const rotationX = -0.07 +
|
|
16477
|
+
Math.cos(timeMs / 3200 + animationPhase * 0.7) * 0.02 -
|
|
16478
|
+
interaction.bodyOffsetY * 0.08 -
|
|
16479
|
+
interaction.gazeY * 0.42;
|
|
16480
|
+
const surfaceOptions = {
|
|
16481
|
+
radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.12,
|
|
16482
|
+
radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1,
|
|
16483
|
+
radiusZ: size *
|
|
16484
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
16485
|
+
(1.04 + (morphologyProfile.body.horizontalStretch - 1) * 0.2),
|
|
16486
|
+
morphologyProfile,
|
|
16487
|
+
timeMs,
|
|
16488
|
+
animationPhase,
|
|
16489
|
+
tentacleProfiles,
|
|
16490
|
+
};
|
|
16491
|
+
const surfacePatches = resolveVisibleBlobbyContinuousPatches({
|
|
16492
|
+
...surfaceOptions,
|
|
16493
|
+
center: meshCenter,
|
|
16494
|
+
rotationX,
|
|
16495
|
+
rotationY,
|
|
16496
|
+
sceneCenterX,
|
|
16497
|
+
sceneCenterY,
|
|
16498
|
+
size,
|
|
16499
|
+
palette,
|
|
16500
|
+
});
|
|
16501
|
+
const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.4 - 0.04, -0.24, 0.08);
|
|
16502
|
+
const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.0, 0.18, 0.32);
|
|
16503
|
+
const mouthLatitude = clampNumber$1(eyeLatitude + 0.21 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
|
|
16504
|
+
const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
|
|
16505
|
+
const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
|
|
16506
|
+
const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
|
|
16507
|
+
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
16508
|
+
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
16509
|
+
drawAvatarFrame(context, size, palette);
|
|
16510
|
+
drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
16511
|
+
drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile);
|
|
16512
|
+
for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
16513
|
+
drawBlobbyContinuousSurfacePatch(context, surfacePatch);
|
|
16514
|
+
}
|
|
16515
|
+
drawBlobbySkinSpots({
|
|
16516
|
+
context,
|
|
16517
|
+
surfaceOptions,
|
|
16518
|
+
center: meshCenter,
|
|
16519
|
+
rotationX,
|
|
16520
|
+
rotationY,
|
|
16521
|
+
sceneCenterX,
|
|
16522
|
+
sceneCenterY,
|
|
16523
|
+
size,
|
|
16524
|
+
palette,
|
|
16525
|
+
skinSpots,
|
|
16526
|
+
});
|
|
16527
|
+
drawBlobbyContinuousCurrents({
|
|
16528
|
+
context,
|
|
16529
|
+
surfaceOptions,
|
|
16530
|
+
center: meshCenter,
|
|
16531
|
+
rotationX,
|
|
16532
|
+
rotationY,
|
|
16533
|
+
sceneCenterX,
|
|
16534
|
+
sceneCenterY,
|
|
16535
|
+
size,
|
|
16536
|
+
palette,
|
|
16537
|
+
morphologyProfile,
|
|
16538
|
+
timeMs,
|
|
16539
|
+
animationPhase,
|
|
16540
|
+
});
|
|
16541
|
+
drawBlobbyContinuousSuckers({
|
|
16542
|
+
context,
|
|
16543
|
+
surfaceOptions,
|
|
16544
|
+
center: meshCenter,
|
|
16545
|
+
rotationX,
|
|
16546
|
+
rotationY,
|
|
16547
|
+
sceneCenterX,
|
|
16548
|
+
sceneCenterY,
|
|
16549
|
+
size,
|
|
16550
|
+
palette,
|
|
16551
|
+
});
|
|
16552
|
+
drawBlobbyContinuousGloss({
|
|
16553
|
+
context,
|
|
16554
|
+
surfaceOptions,
|
|
16555
|
+
center: meshCenter,
|
|
16556
|
+
rotationX,
|
|
16557
|
+
rotationY,
|
|
16558
|
+
sceneCenterX,
|
|
16559
|
+
sceneCenterY,
|
|
16560
|
+
size,
|
|
16561
|
+
palette,
|
|
16562
|
+
});
|
|
16563
|
+
drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
16564
|
+
drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
16565
|
+
drawProjectedOrganicMouth(context, [
|
|
16566
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
16567
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
16568
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
|
|
16569
|
+
], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
|
|
16570
|
+
},
|
|
16571
|
+
};
|
|
16572
|
+
/**
|
|
16573
|
+
* Creates seeded tentacle-lobe profiles around the visible lower octopus body.
|
|
16574
|
+
*
|
|
16575
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16576
|
+
*/
|
|
16577
|
+
function createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
16578
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
|
|
16579
|
+
const tentacleRandom = createRandom(`octopus3d4-tentacle-${tentacleIndex}`);
|
|
16580
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
|
|
16581
|
+
return {
|
|
16582
|
+
centerLongitude: -Math.PI * 0.9 +
|
|
16583
|
+
progress * Math.PI * 1.8 +
|
|
16584
|
+
(tentacleRandom() - 0.5) * (0.06 + morphologyProfile.tentacles.rootSpreadScale * 0.025),
|
|
16585
|
+
widthScale: 0.92 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.18,
|
|
16586
|
+
lengthScale: 0.9 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.24,
|
|
16587
|
+
swayScale: 0.86 + tentacleRandom() * 0.4 + (morphologyProfile.tentacles.swayScale - 1) * 0.22,
|
|
16588
|
+
depthScale: 0.9 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.22,
|
|
16589
|
+
curlScale: 0.62 + tentacleRandom() * 0.46,
|
|
16590
|
+
primaryPhase: tentacleRandom() * Math.PI * 2,
|
|
16591
|
+
secondaryPhase: tentacleRandom() * Math.PI * 2,
|
|
16592
|
+
suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
|
|
16593
|
+
};
|
|
16594
|
+
});
|
|
16595
|
+
}
|
|
16596
|
+
/**
|
|
16597
|
+
* Creates seeded skin pigment spots distributed across the upper octopus mesh.
|
|
16598
|
+
*
|
|
16599
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16600
|
+
*/
|
|
16601
|
+
function createBlobbySkinSpots(createRandom) {
|
|
16602
|
+
const spotRandom = createRandom('octopus3d4-skin-spots');
|
|
16603
|
+
return Array.from({ length: SKIN_SPOT_COUNT }, () => ({
|
|
16604
|
+
latitude: -0.45 + spotRandom() * 0.7,
|
|
16605
|
+
longitude: -0.5 + spotRandom() * 1.0,
|
|
16606
|
+
radiusScale: 0.0028 + spotRandom() * 0.0052,
|
|
16607
|
+
opacity: 0.16 + spotRandom() * 0.22,
|
|
16608
|
+
}));
|
|
16609
|
+
}
|
|
16610
|
+
/**
|
|
16611
|
+
* Draws the soft underwater atmosphere behind the continuous octopus mesh.
|
|
16612
|
+
*
|
|
16613
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16614
|
+
*/
|
|
16615
|
+
function drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
16616
|
+
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));
|
|
16617
|
+
glowGradient.addColorStop(0, `${palette.highlight}74`);
|
|
16618
|
+
glowGradient.addColorStop(0.32, `${palette.accent}32`);
|
|
16619
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
16620
|
+
context.fillStyle = glowGradient;
|
|
16621
|
+
context.fillRect(0, 0, size, size);
|
|
16622
|
+
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);
|
|
16623
|
+
lowerGradient.addColorStop(0, `${palette.secondary}28`);
|
|
16624
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
16625
|
+
context.fillStyle = lowerGradient;
|
|
16626
|
+
context.fillRect(0, 0, size, size);
|
|
16627
|
+
}
|
|
16628
|
+
/**
|
|
16629
|
+
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
16630
|
+
*
|
|
16631
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16632
|
+
*/
|
|
16633
|
+
function drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
16634
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.046;
|
|
16635
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
16636
|
+
const rx = size * (0.2 + morphologyProfile.tentacles.rootSpreadScale * 0.024 + interaction.intensity * 0.022);
|
|
16637
|
+
const ry = size * 0.062;
|
|
16638
|
+
context.save();
|
|
16639
|
+
context.translate(cx, cy);
|
|
16640
|
+
context.scale(1, ry / rx);
|
|
16641
|
+
const blurRadius = rx * 1.42;
|
|
16642
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
16643
|
+
shadowGradient.addColorStop(0, `${palette.shadow}82`);
|
|
16644
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}4a`);
|
|
16645
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1c`);
|
|
16646
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
16647
|
+
context.fillStyle = shadowGradient;
|
|
16648
|
+
context.beginPath();
|
|
16649
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
16650
|
+
context.fill();
|
|
16651
|
+
context.restore();
|
|
16652
|
+
}
|
|
16653
|
+
/**
|
|
16654
|
+
* Number of latitude segments used by the continuous Octopus 3D 4 mesh.
|
|
16655
|
+
*
|
|
16656
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16657
|
+
*/
|
|
16658
|
+
const LATITUDE_PATCH_COUNT = 20;
|
|
16659
|
+
/**
|
|
16660
|
+
* Number of longitude segments used by the continuous Octopus 3D 4 mesh.
|
|
16661
|
+
*
|
|
16662
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16663
|
+
*/
|
|
16664
|
+
const LONGITUDE_PATCH_COUNT = 48;
|
|
16665
|
+
/**
|
|
16666
|
+
* Resolves visible projected patches for the continuous Octopus 3D 4 mesh.
|
|
16667
|
+
*
|
|
16668
|
+
* Within a single frame, mesh corner samples and longitude-only computations (tentacle
|
|
16669
|
+
* influence and lobe wave) are quantized to the patch grid and computed once each rather
|
|
16670
|
+
* than re-evaluated for every patch corner.
|
|
16671
|
+
*
|
|
16672
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16673
|
+
*/
|
|
16674
|
+
function resolveVisibleBlobbyContinuousPatches(options) {
|
|
16675
|
+
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
16676
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT;
|
|
16677
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT;
|
|
16678
|
+
const surfacePatches = [];
|
|
16679
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
16680
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
16681
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
16682
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
16683
|
+
}
|
|
16684
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
16685
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
16686
|
+
}
|
|
16687
|
+
const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
|
|
16688
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
16689
|
+
const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
|
|
16690
|
+
const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
16691
|
+
const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
16692
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
16693
|
+
const cornerLongitude = longitudeBoundaries[boundaryIndex];
|
|
16694
|
+
cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveBlobbyTentacleInfluence(options, cornerLongitude);
|
|
16695
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveBlobbyLobeWave(options, cornerLongitude);
|
|
16696
|
+
}
|
|
16697
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
16698
|
+
const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
|
|
16699
|
+
cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyTentacleInfluence(options, patchCenterLongitude);
|
|
16700
|
+
cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyLobeWave(options, patchCenterLongitude);
|
|
16701
|
+
cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
|
|
16702
|
+
}
|
|
16703
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
16704
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
16705
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
16706
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
16707
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
16708
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
16709
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
16710
|
+
const cornerSample = sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
16711
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
16712
|
+
}
|
|
16713
|
+
}
|
|
16714
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
16715
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
16716
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
16717
|
+
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
16718
|
+
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
16719
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
16720
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
16721
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
16722
|
+
const transformedCorners = [
|
|
16723
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
16724
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
16725
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
16726
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
16727
|
+
];
|
|
16728
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
16729
|
+
if (surfaceNormal.z <= 0.006) {
|
|
16730
|
+
continue;
|
|
16731
|
+
}
|
|
16732
|
+
const projectedCorners = [
|
|
16733
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
16734
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
16735
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
16736
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
16737
|
+
];
|
|
16738
|
+
const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
|
|
16739
|
+
const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
|
|
16740
|
+
surfacePatches.push({
|
|
16741
|
+
corners: projectedCorners,
|
|
16742
|
+
averageDepth: (transformedCorners[0].z +
|
|
16743
|
+
transformedCorners[1].z +
|
|
16744
|
+
transformedCorners[2].z +
|
|
16745
|
+
transformedCorners[3].z) /
|
|
16746
|
+
4,
|
|
16747
|
+
lightIntensity,
|
|
16748
|
+
rimLightIntensity,
|
|
16749
|
+
fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
16750
|
+
outlineColor: verticalProgress < 0.52 ? `${palette.highlight}5c` : `${palette.shadow}66`,
|
|
16751
|
+
});
|
|
16752
|
+
}
|
|
16753
|
+
}
|
|
16754
|
+
return surfacePatches;
|
|
16755
|
+
}
|
|
16756
|
+
/**
|
|
16757
|
+
* Samples one point on the continuous Octopus 3D 4 surface.
|
|
16758
|
+
*
|
|
16759
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16760
|
+
*/
|
|
16761
|
+
function sampleBlobbyContinuousSurfacePoint(options, latitude, longitude) {
|
|
16762
|
+
return sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, resolveBlobbyTentacleInfluence(options, longitude), resolveBlobbyLobeWave(options, longitude));
|
|
16763
|
+
}
|
|
16764
|
+
/**
|
|
16765
|
+
* Samples one point on the continuous Octopus 3D 4 surface using precomputed longitude-only values.
|
|
16766
|
+
*
|
|
16767
|
+
* The patch loop quantizes the mesh into a fixed corner grid, so the same longitude is reused
|
|
16768
|
+
* across every latitude row and each tentacle/lobe value is computed once per frame instead
|
|
16769
|
+
* of `latitudePatchCount * 4` times.
|
|
16770
|
+
*
|
|
16771
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16772
|
+
*/
|
|
16773
|
+
function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
|
|
16774
|
+
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
16775
|
+
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
16776
|
+
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
16777
|
+
const upperBlend = Math.pow(1 - verticalProgress, 1.32);
|
|
16778
|
+
const lowerBlend = smoothStep(0.34, 1, verticalProgress);
|
|
16779
|
+
const tipBlend = smoothStep(0.66, 1, verticalProgress);
|
|
16780
|
+
const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
|
|
16781
|
+
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.26 + tipBlend * 0.22);
|
|
16782
|
+
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
16783
|
+
animationPhase * 0.6 +
|
|
16784
|
+
timeMs / (1700 + morphologyProfile.body.lobeCount * 28)) *
|
|
16785
|
+
(0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.78) *
|
|
16786
|
+
(0.32 + lowerBlend * 0.7);
|
|
16787
|
+
const primaryTentacleWave = Math.sin(timeMs / 740 + tentacleInfluence.primaryPhase + verticalProgress * 2.6) *
|
|
16788
|
+
lowerBlend *
|
|
16789
|
+
tentacleInfluence.core *
|
|
16790
|
+
tentacleInfluence.swayScale;
|
|
16791
|
+
const secondaryTentacleWave = Math.sin(timeMs / 470 + tentacleInfluence.secondaryPhase + verticalProgress * 4.2) *
|
|
16792
|
+
lowerBlend *
|
|
16793
|
+
tentacleInfluence.core *
|
|
16794
|
+
tentacleInfluence.swayScale *
|
|
16795
|
+
0.42;
|
|
16796
|
+
const tentacleCurl = Math.sin(timeMs / 1180 + tentacleInfluence.primaryPhase * 0.6 + verticalProgress * 3.2) *
|
|
16797
|
+
tipBlend *
|
|
16798
|
+
tentacleInfluence.core *
|
|
16799
|
+
tentacleInfluence.curlScale *
|
|
16800
|
+
0.36;
|
|
16801
|
+
const horizontalScale = 1.06 +
|
|
16802
|
+
mantleRipple +
|
|
16803
|
+
lowerBlend * (0.18 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.12) +
|
|
16804
|
+
lowerBlend * tentacleInfluence.core * (0.22 + lowerLobeWave * 0.14) -
|
|
16805
|
+
upperBlend * 0.08;
|
|
16806
|
+
const depthScale = 1.08 +
|
|
16807
|
+
upperBlend * 0.16 +
|
|
16808
|
+
Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
|
|
16809
|
+
lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
|
|
16810
|
+
Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
|
|
16811
|
+
const tentacleTubeRadius = lowerBlend *
|
|
16812
|
+
tentacleInfluence.core *
|
|
16813
|
+
(0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) *
|
|
16814
|
+
radiusX;
|
|
16815
|
+
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
16816
|
+
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
|
|
16817
|
+
const lowerDrop = lowerBlend *
|
|
16818
|
+
radiusY *
|
|
16819
|
+
(0.2 +
|
|
16820
|
+
tentacleInfluence.core *
|
|
16821
|
+
(0.42 +
|
|
16822
|
+
tentacleInfluence.lengthScale * 0.24 +
|
|
16823
|
+
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
|
|
16824
|
+
const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
|
|
16825
|
+
return {
|
|
16826
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX +
|
|
16827
|
+
combinedTentacleSway +
|
|
16828
|
+
tentacleCurl * radiusX * 0.18,
|
|
16829
|
+
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
|
|
16830
|
+
upperBlend * radiusY * 0.12 +
|
|
16831
|
+
lowerDrop +
|
|
16832
|
+
Math.sin(timeMs / 1380 + animationPhase + latitude * 1.5) * lowerBlend * radiusY * 0.022 +
|
|
16833
|
+
Math.cos(timeMs / 820 + tentacleInfluence.primaryPhase) *
|
|
16834
|
+
lowerBlend *
|
|
16835
|
+
tipBlend *
|
|
16836
|
+
tentacleInfluence.core *
|
|
16837
|
+
radiusY *
|
|
16838
|
+
0.04,
|
|
16839
|
+
z: Math.cos(effectiveLongitude) * planarRadiusZ +
|
|
16840
|
+
Math.cos(timeMs / 960 + tentacleInfluence.primaryPhase + verticalProgress) *
|
|
16841
|
+
lowerBlend *
|
|
16842
|
+
tentacleInfluence.core *
|
|
16843
|
+
radiusZ *
|
|
16844
|
+
0.044 +
|
|
16845
|
+
tentacleCurl * radiusZ * 0.14,
|
|
16846
|
+
};
|
|
16847
|
+
}
|
|
16848
|
+
/**
|
|
16849
|
+
* Blends nearby seeded tentacle profiles at one mesh longitude.
|
|
16850
|
+
*
|
|
16851
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16852
|
+
*/
|
|
16853
|
+
function resolveBlobbyTentacleInfluence(options, longitude) {
|
|
16854
|
+
let totalWeight = 0;
|
|
16855
|
+
let weightedSin = 0;
|
|
16856
|
+
let weightedCos = 0;
|
|
16857
|
+
let weightedWidthScale = 0;
|
|
16858
|
+
let weightedLengthScale = 0;
|
|
16859
|
+
let weightedSwayScale = 0;
|
|
16860
|
+
let weightedDepthScale = 0;
|
|
16861
|
+
let weightedCurlScale = 0;
|
|
16862
|
+
let weightedPrimaryPhase = 0;
|
|
16863
|
+
let weightedSecondaryPhase = 0;
|
|
16864
|
+
for (const tentacleProfile of options.tentacleProfiles) {
|
|
16865
|
+
const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
|
|
16866
|
+
const width = 0.22 * tentacleProfile.widthScale;
|
|
16867
|
+
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
16868
|
+
totalWeight += weight;
|
|
16869
|
+
weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
|
|
16870
|
+
weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
|
|
16871
|
+
weightedWidthScale += tentacleProfile.widthScale * weight;
|
|
16872
|
+
weightedLengthScale += tentacleProfile.lengthScale * weight;
|
|
16873
|
+
weightedSwayScale += tentacleProfile.swayScale * weight;
|
|
16874
|
+
weightedDepthScale += tentacleProfile.depthScale * weight;
|
|
16875
|
+
weightedCurlScale += tentacleProfile.curlScale * weight;
|
|
16876
|
+
weightedPrimaryPhase += tentacleProfile.primaryPhase * weight;
|
|
16877
|
+
weightedSecondaryPhase += tentacleProfile.secondaryPhase * weight;
|
|
16878
|
+
}
|
|
16879
|
+
if (totalWeight < 0.0001) {
|
|
16880
|
+
return {
|
|
16881
|
+
core: 0,
|
|
16882
|
+
centerLongitude: longitude,
|
|
16883
|
+
widthScale: 1,
|
|
16884
|
+
lengthScale: 1,
|
|
16885
|
+
swayScale: 1,
|
|
16886
|
+
depthScale: 1,
|
|
16887
|
+
curlScale: 1,
|
|
16888
|
+
primaryPhase: 0,
|
|
16889
|
+
secondaryPhase: 0,
|
|
16890
|
+
};
|
|
16891
|
+
}
|
|
16892
|
+
return {
|
|
16893
|
+
core: clampNumber$1(totalWeight, 0, 1),
|
|
16894
|
+
centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
|
|
16895
|
+
widthScale: weightedWidthScale / totalWeight,
|
|
16896
|
+
lengthScale: weightedLengthScale / totalWeight,
|
|
16897
|
+
swayScale: weightedSwayScale / totalWeight,
|
|
16898
|
+
depthScale: weightedDepthScale / totalWeight,
|
|
16899
|
+
curlScale: weightedCurlScale / totalWeight,
|
|
16900
|
+
primaryPhase: weightedPrimaryPhase / totalWeight,
|
|
16901
|
+
secondaryPhase: weightedSecondaryPhase / totalWeight,
|
|
16902
|
+
};
|
|
16903
|
+
}
|
|
16904
|
+
/**
|
|
16905
|
+
* Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
|
|
16906
|
+
*
|
|
16907
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16908
|
+
*/
|
|
16909
|
+
function resolveBlobbyLobeWave(options, longitude) {
|
|
16910
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
16911
|
+
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
|
|
16912
|
+
animationPhase +
|
|
16913
|
+
timeMs / (940 + morphologyProfile.body.lobeCount * 18)) +
|
|
16914
|
+
1) /
|
|
16915
|
+
2);
|
|
16916
|
+
}
|
|
16917
|
+
/**
|
|
16918
|
+
* Resolves one base fill tone for a patch on the continuous octopus mesh.
|
|
16919
|
+
*
|
|
16920
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16921
|
+
*/
|
|
16922
|
+
function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
|
|
16923
|
+
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
|
|
16924
|
+
if (tonalProgress < 0.12) {
|
|
16925
|
+
return palette.highlight;
|
|
16926
|
+
}
|
|
16927
|
+
if (tonalProgress < 0.3) {
|
|
16928
|
+
return palette.secondary;
|
|
16929
|
+
}
|
|
16930
|
+
if (tonalProgress < 0.72) {
|
|
16931
|
+
return forwardness > 0.56 ? palette.secondary : palette.primary;
|
|
16932
|
+
}
|
|
16933
|
+
return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
|
|
16934
|
+
}
|
|
16935
|
+
/**
|
|
16936
|
+
* Draws one projected mesh patch with multi-pass shading and a soft edge.
|
|
16937
|
+
*
|
|
16938
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16939
|
+
*/
|
|
16940
|
+
function drawBlobbyContinuousSurfacePatch(context, surfacePatch) {
|
|
16941
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
16942
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
16943
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.2 * surfacePatch.lightIntensity})`);
|
|
16944
|
+
}
|
|
16945
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
16946
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.26 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
16947
|
+
}
|
|
16948
|
+
if (surfacePatch.rimLightIntensity > 0.04) {
|
|
16949
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 247, 230, ${0.22 * surfacePatch.rimLightIntensity})`);
|
|
16950
|
+
}
|
|
16951
|
+
context.save();
|
|
16952
|
+
context.beginPath();
|
|
16953
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
16954
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
16955
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
16956
|
+
}
|
|
16957
|
+
context.closePath();
|
|
16958
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
16959
|
+
context.lineWidth = Math.max(0.6, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0026);
|
|
16960
|
+
context.lineJoin = 'round';
|
|
16961
|
+
context.stroke();
|
|
16962
|
+
context.restore();
|
|
16963
|
+
}
|
|
16964
|
+
/**
|
|
16965
|
+
* Draws projected mantle-current lines on the front of the mesh.
|
|
16966
|
+
*
|
|
16967
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
16968
|
+
*/
|
|
16969
|
+
function drawBlobbyContinuousCurrents(options) {
|
|
16970
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
|
|
16971
|
+
const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
|
|
16972
|
+
const centerIndex = (currentCount - 1) / 2;
|
|
16973
|
+
context.save();
|
|
16974
|
+
context.lineCap = 'round';
|
|
16975
|
+
context.lineJoin = 'round';
|
|
16976
|
+
for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
|
|
16977
|
+
const baseLongitude = (currentIndex - centerIndex) * 0.15;
|
|
16978
|
+
const projectedPoints = [];
|
|
16979
|
+
for (let sampleIndex = 0; sampleIndex < 9; sampleIndex++) {
|
|
16980
|
+
const progress = sampleIndex / 8;
|
|
16981
|
+
const latitude = -0.48 + progress * 0.78;
|
|
16982
|
+
const longitude = baseLongitude + Math.sin(timeMs / 1140 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.038;
|
|
16983
|
+
const scenePoint = transformScenePoint(sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
|
|
16984
|
+
if (scenePoint.z > center.z - size * 0.016) {
|
|
16985
|
+
projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
16986
|
+
}
|
|
16987
|
+
}
|
|
16988
|
+
if (projectedPoints.length < 3) {
|
|
16989
|
+
continue;
|
|
16990
|
+
}
|
|
16991
|
+
context.beginPath();
|
|
16992
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
16993
|
+
for (const projectedPoint of projectedPoints.slice(1)) {
|
|
16994
|
+
context.lineTo(projectedPoint.x, projectedPoint.y);
|
|
16995
|
+
}
|
|
16996
|
+
context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
|
|
16997
|
+
context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
|
|
16998
|
+
context.stroke();
|
|
16999
|
+
}
|
|
17000
|
+
context.restore();
|
|
17001
|
+
}
|
|
17002
|
+
/**
|
|
17003
|
+
* Draws small projected sucker highlights on the waving lower mesh lobes.
|
|
17004
|
+
*
|
|
17005
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
17006
|
+
*/
|
|
17007
|
+
function drawBlobbyContinuousSuckers(options) {
|
|
17008
|
+
const { surfaceOptions, size } = options;
|
|
17009
|
+
const { timeMs } = surfaceOptions;
|
|
17010
|
+
for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
|
|
17011
|
+
if (Math.cos(tentacleProfile.centerLongitude) < -0.16) {
|
|
17012
|
+
continue;
|
|
17013
|
+
}
|
|
17014
|
+
for (let suckerIndex = 0; suckerIndex < 4; suckerIndex++) {
|
|
17015
|
+
const latitude = 0.5 + suckerIndex * 0.12;
|
|
17016
|
+
const sideOffset = tentacleProfile.suckerSide * (0.038 + suckerIndex * 0.014) * tentacleProfile.widthScale;
|
|
17017
|
+
const waveOffset = Math.sin(timeMs / 880 + tentacleProfile.primaryPhase + suckerIndex * 0.78) * 0.02;
|
|
17018
|
+
drawBlobbyContinuousSurfaceSpot({
|
|
17019
|
+
...options,
|
|
17020
|
+
latitude,
|
|
17021
|
+
longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
|
|
17022
|
+
radiusScale: size * (0.0068 - suckerIndex * 0.0006),
|
|
17023
|
+
});
|
|
17024
|
+
}
|
|
17025
|
+
}
|
|
17026
|
+
}
|
|
17027
|
+
/**
|
|
17028
|
+
* Draws seeded pigment spots across the upper mesh for a richer skin texture.
|
|
17029
|
+
*
|
|
17030
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
17031
|
+
*/
|
|
17032
|
+
function drawBlobbySkinSpots(options) {
|
|
17033
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, skinSpots, } = options;
|
|
17034
|
+
for (const skinSpot of skinSpots) {
|
|
17035
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, skinSpot.latitude, skinSpot.longitude);
|
|
17036
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
17037
|
+
if (sceneCenterPoint.z <= center.z - size * 0.01) {
|
|
17038
|
+
continue;
|
|
17039
|
+
}
|
|
17040
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
17041
|
+
const spotRadius = size * skinSpot.radiusScale;
|
|
17042
|
+
context.save();
|
|
17043
|
+
context.beginPath();
|
|
17044
|
+
context.arc(projectedCenterPoint.x, projectedCenterPoint.y, spotRadius, 0, Math.PI * 2);
|
|
17045
|
+
context.fillStyle = `${palette.shadow}${formatAlphaHex(skinSpot.opacity)}`;
|
|
17046
|
+
context.fill();
|
|
17047
|
+
context.restore();
|
|
17048
|
+
}
|
|
17049
|
+
}
|
|
17050
|
+
/**
|
|
17051
|
+
* Draws one tiny projected surface spot by sampling local mesh tangents.
|
|
17052
|
+
*
|
|
17053
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
17054
|
+
*/
|
|
17055
|
+
function drawBlobbyContinuousSurfaceSpot(options) {
|
|
17056
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
|
|
17057
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude);
|
|
17058
|
+
const localHorizontal = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
|
|
17059
|
+
const localVertical = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
|
|
17060
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
17061
|
+
if (sceneCenterPoint.z <= center.z - size * 0.012) {
|
|
17062
|
+
return;
|
|
17063
|
+
}
|
|
17064
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
17065
|
+
const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
17066
|
+
const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
17067
|
+
const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
|
|
17068
|
+
radiusScale *
|
|
17069
|
+
0.78, size * 0.003, size * 0.02);
|
|
17070
|
+
const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
|
|
17071
|
+
radiusScale *
|
|
17072
|
+
0.54, size * 0.0024, size * 0.015);
|
|
17073
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
17074
|
+
context.save();
|
|
17075
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
17076
|
+
context.rotate(rotation);
|
|
17077
|
+
context.beginPath();
|
|
17078
|
+
context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
|
|
17079
|
+
context.fillStyle = `${palette.highlight}80`;
|
|
17080
|
+
context.fill();
|
|
17081
|
+
context.strokeStyle = `${palette.highlight}a8`;
|
|
17082
|
+
context.lineWidth = Math.max(0.7, size * 0.0028);
|
|
17083
|
+
context.stroke();
|
|
17084
|
+
context.restore();
|
|
17085
|
+
}
|
|
17086
|
+
/**
|
|
17087
|
+
* Draws a soft, slowly drifting gloss highlight on the front of the mesh.
|
|
17088
|
+
*
|
|
17089
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
17090
|
+
*/
|
|
17091
|
+
function drawBlobbyContinuousGloss(options) {
|
|
17092
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size } = options;
|
|
17093
|
+
const { timeMs, animationPhase } = surfaceOptions;
|
|
17094
|
+
const glossLatitude = -0.3 + Math.sin(timeMs / 2700 + animationPhase) * 0.04;
|
|
17095
|
+
const glossLongitude = -0.18 + Math.cos(timeMs / 2300 + animationPhase * 0.8) * 0.05;
|
|
17096
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, glossLatitude, glossLongitude);
|
|
17097
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
17098
|
+
if (sceneCenterPoint.z <= center.z) {
|
|
17099
|
+
return;
|
|
17100
|
+
}
|
|
17101
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
17102
|
+
const glossRadius = size * 0.058;
|
|
17103
|
+
context.save();
|
|
17104
|
+
const glossGradient = context.createRadialGradient(projectedCenterPoint.x - glossRadius * 0.3, projectedCenterPoint.y - glossRadius * 0.4, glossRadius * 0.04, projectedCenterPoint.x, projectedCenterPoint.y, glossRadius);
|
|
17105
|
+
glossGradient.addColorStop(0, 'rgba(255, 255, 255, 0.34)');
|
|
17106
|
+
glossGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.1)');
|
|
17107
|
+
glossGradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
|
|
17108
|
+
context.fillStyle = glossGradient;
|
|
17109
|
+
context.beginPath();
|
|
17110
|
+
context.ellipse(projectedCenterPoint.x, projectedCenterPoint.y, glossRadius * 1.05, glossRadius * 0.78, 0, 0, Math.PI * 2);
|
|
17111
|
+
context.fill();
|
|
17112
|
+
context.restore();
|
|
17113
|
+
}
|
|
17114
|
+
/**
|
|
17115
|
+
* Resolves a signed angular distance from the source longitude to the target longitude.
|
|
17116
|
+
*
|
|
17117
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
17118
|
+
*/
|
|
17119
|
+
function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
|
|
17120
|
+
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
17121
|
+
}
|
|
17122
|
+
/**
|
|
17123
|
+
* Smoothly maps a value between two bounds into `[0, 1]`.
|
|
17124
|
+
*
|
|
17125
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
17126
|
+
*/
|
|
17127
|
+
function smoothStep(edgeStart, edgeEnd, value) {
|
|
17128
|
+
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
17129
|
+
return progress * progress * (3 - 2 * progress);
|
|
17130
|
+
}
|
|
17131
|
+
/**
|
|
17132
|
+
* Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
|
|
17133
|
+
*
|
|
17134
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
17135
|
+
*/
|
|
17136
|
+
function formatAlphaHex(opacity) {
|
|
17137
|
+
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
17138
|
+
.toString(16)
|
|
17139
|
+
.padStart(2, '0');
|
|
17140
|
+
}
|
|
17141
|
+
|
|
15593
17142
|
/* eslint-disable no-magic-numbers */
|
|
15594
17143
|
/**
|
|
15595
17144
|
* Octopus avatar visual.
|
|
@@ -16360,6 +17909,8 @@ const AVATAR_VISUALS = [
|
|
|
16360
17909
|
octopus3AvatarVisual,
|
|
16361
17910
|
octopus3dAvatarVisual,
|
|
16362
17911
|
octopus3d2AvatarVisual,
|
|
17912
|
+
octopus3d3AvatarVisual,
|
|
17913
|
+
octopus3d4AvatarVisual,
|
|
16363
17914
|
asciiOctopusAvatarVisual,
|
|
16364
17915
|
minecraftAvatarVisual,
|
|
16365
17916
|
minecraft2AvatarVisual,
|
|
@@ -18648,7 +20199,15 @@ function buildTeammateMetadata(entry) {
|
|
|
18648
20199
|
* Builds the teammate request text, optionally including context.
|
|
18649
20200
|
*/
|
|
18650
20201
|
function buildTeammateRequest(message, context) {
|
|
18651
|
-
|
|
20202
|
+
if (!context) {
|
|
20203
|
+
return message;
|
|
20204
|
+
}
|
|
20205
|
+
return spaceTrim$1((block) => `
|
|
20206
|
+
${block(message)}
|
|
20207
|
+
|
|
20208
|
+
Context:
|
|
20209
|
+
${block(context)}
|
|
20210
|
+
`);
|
|
18652
20211
|
}
|
|
18653
20212
|
/**
|
|
18654
20213
|
* Builds a minimal chat prompt for teammate calls.
|
|
@@ -22474,7 +24033,11 @@ function createUseProjectToolFunctions() {
|
|
|
22474
24033
|
const lineRangedContent = applyOptionalLineRange(decodedContent, args.startLine, args.endLine);
|
|
22475
24034
|
const wasCharacterTruncated = lineRangedContent.length > MAX_PROJECT_FILE_CONTENT_CHARACTERS;
|
|
22476
24035
|
const contentToReturn = wasCharacterTruncated
|
|
22477
|
-
?
|
|
24036
|
+
? spaceTrim$1((block) => `
|
|
24037
|
+
${block(lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS))}
|
|
24038
|
+
|
|
24039
|
+
[...truncated...]
|
|
24040
|
+
`)
|
|
22478
24041
|
: lineRangedContent;
|
|
22479
24042
|
const wasTruncated = decoded.isTruncated || wasCharacterTruncated;
|
|
22480
24043
|
if (wasCharacterTruncated) {
|
|
@@ -26010,11 +27573,19 @@ const JsonFileImportPlugin = {
|
|
|
26010
27573
|
try {
|
|
26011
27574
|
const json = JSON.parse(content);
|
|
26012
27575
|
const formattedJson = JSON.stringify(json, null, 4);
|
|
26013
|
-
return
|
|
27576
|
+
return spaceTrim$1((block) => `
|
|
27577
|
+
\`\`\`json
|
|
27578
|
+
${block(formattedJson)}
|
|
27579
|
+
\`\`\`
|
|
27580
|
+
`);
|
|
26014
27581
|
}
|
|
26015
27582
|
catch (error) {
|
|
26016
27583
|
// If JSON is invalid, still import it but maybe not as pretty JSON
|
|
26017
|
-
return
|
|
27584
|
+
return spaceTrim$1((block) => `
|
|
27585
|
+
\`\`\`json
|
|
27586
|
+
${block(content)}
|
|
27587
|
+
\`\`\`
|
|
27588
|
+
`);
|
|
26018
27589
|
}
|
|
26019
27590
|
},
|
|
26020
27591
|
};
|
|
@@ -26038,7 +27609,11 @@ const TextFileImportPlugin = {
|
|
|
26038
27609
|
import(content, mimeType) {
|
|
26039
27610
|
const extension = mimeTypeToExtension(mimeType);
|
|
26040
27611
|
const codeBlockType = extension || 'txt';
|
|
26041
|
-
return
|
|
27612
|
+
return spaceTrim$1((block) => `
|
|
27613
|
+
\`\`\`${codeBlockType}
|
|
27614
|
+
${block(content)}
|
|
27615
|
+
\`\`\`
|
|
27616
|
+
`);
|
|
26042
27617
|
},
|
|
26043
27618
|
};
|
|
26044
27619
|
|
|
@@ -26285,11 +27860,19 @@ function collectExampleInteractionLines(parseResult, samples) {
|
|
|
26285
27860
|
const examples = [];
|
|
26286
27861
|
const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
|
|
26287
27862
|
if (initialMessage) {
|
|
26288
|
-
examples.push(
|
|
27863
|
+
examples.push(spaceTrim$1((block) => `
|
|
27864
|
+
**Agent:**
|
|
27865
|
+
${block(initialMessage)}
|
|
27866
|
+
`));
|
|
26289
27867
|
}
|
|
26290
27868
|
if (samples && samples.length > 0) {
|
|
26291
27869
|
for (const sample of samples) {
|
|
26292
|
-
examples.push(
|
|
27870
|
+
examples.push(spaceTrim$1((block) => `
|
|
27871
|
+
**User:** ${block(String(sample.question))}
|
|
27872
|
+
|
|
27873
|
+
**Agent:**
|
|
27874
|
+
${block(sample.answer)}
|
|
27875
|
+
`));
|
|
26293
27876
|
}
|
|
26294
27877
|
}
|
|
26295
27878
|
return examples;
|
|
@@ -28004,7 +29587,11 @@ function removeLeadingTopLevelHeading(markdown) {
|
|
|
28004
29587
|
* @private internal utility of `createStandaloneBookLanguageMarkdown`
|
|
28005
29588
|
*/
|
|
28006
29589
|
function renderDocumentationSection(title, documentation) {
|
|
28007
|
-
return
|
|
29590
|
+
return spaceTrim$1((block) => `
|
|
29591
|
+
#### ${title}
|
|
29592
|
+
|
|
29593
|
+
${block(removeLeadingTopLevelHeading(documentation))}
|
|
29594
|
+
`);
|
|
28008
29595
|
}
|
|
28009
29596
|
/**
|
|
28010
29597
|
* Renders the documentation body for one grouped commitment entry.
|
|
@@ -28655,7 +30242,9 @@ function parseBookMessageHeader(line) {
|
|
|
28655
30242
|
* @private internal utility of `Book`
|
|
28656
30243
|
*/
|
|
28657
30244
|
function parseCommitmentHeader(line) {
|
|
28658
|
-
|
|
30245
|
+
// Require at least 2 characters in the first keyword word to avoid treating common
|
|
30246
|
+
// single-letter words (e.g. "V" in Czech, "I" or "A" in English) as commitment headers.
|
|
30247
|
+
const match = /^([A-Z][A-Z0-9]+(?: [A-Z0-9]+)*)(?:\s+(.*))?$/u.exec(line);
|
|
28659
30248
|
if (!match) {
|
|
28660
30249
|
return null;
|
|
28661
30250
|
}
|
|
@@ -28851,6 +30440,18 @@ function normalizeHistoryVersionName(versionName) {
|
|
|
28851
30440
|
const normalizedVersionName = versionName.trim();
|
|
28852
30441
|
return normalizedVersionName.length > 0 ? normalizedVersionName : null;
|
|
28853
30442
|
}
|
|
30443
|
+
/**
|
|
30444
|
+
* Builds a Supabase `.or()` filter for agent name or permanent id lookups.
|
|
30445
|
+
*
|
|
30446
|
+
* @param agentNameOrPermanentId - Agent name or stable permanent identifier to match.
|
|
30447
|
+
* @returns `.or()` filter string safe to pass to Supabase.
|
|
30448
|
+
*
|
|
30449
|
+
* @private internal helper of `AgentCollectionInSupabase`
|
|
30450
|
+
*/
|
|
30451
|
+
function buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId) {
|
|
30452
|
+
const encodedAgentIdentifier = encodeURIComponent(agentNameOrPermanentId);
|
|
30453
|
+
return `agentName.eq.${encodedAgentIdentifier},permanentId.eq.${encodedAgentIdentifier}`;
|
|
30454
|
+
}
|
|
28854
30455
|
/**
|
|
28855
30456
|
* Agent collection stored in a Supabase table.
|
|
28856
30457
|
*
|
|
@@ -28898,21 +30499,63 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱🚀] implements AgentCol
|
|
|
28898
30499
|
if (isVerbose) {
|
|
28899
30500
|
console.info(`Found ${selectResult.data.length} agents in directory`);
|
|
28900
30501
|
}
|
|
28901
|
-
return selectResult.data.map((
|
|
28902
|
-
|
|
28903
|
-
|
|
30502
|
+
return selectResult.data.map((row) => this.mapAgentBasicInformationRow(row, isVerbose));
|
|
30503
|
+
}
|
|
30504
|
+
/**
|
|
30505
|
+
* Finds one active agent profile by its human-readable name or permanent id.
|
|
30506
|
+
*
|
|
30507
|
+
* This keeps route-level lookups from loading the whole agent collection when only
|
|
30508
|
+
* one canonical route identifier is needed.
|
|
30509
|
+
*
|
|
30510
|
+
* @param agentNameOrPermanentId - Agent name or stable permanent identifier.
|
|
30511
|
+
* @returns Matching active agent profile or `null` when not found.
|
|
30512
|
+
*
|
|
30513
|
+
* @public exported from `@promptbook/core`
|
|
30514
|
+
*/
|
|
30515
|
+
async findAgentBasicInformation(agentNameOrPermanentId) {
|
|
30516
|
+
var _a;
|
|
30517
|
+
const { isVerbose = DEFAULT_IS_VERBOSE } = this.options || {};
|
|
30518
|
+
const selectResult = await this.supabaseClient
|
|
30519
|
+
.from(this.getTableName('Agent'))
|
|
30520
|
+
.select('agentName,agentProfile,permanentId')
|
|
30521
|
+
.or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
|
|
30522
|
+
.is('deletedAt', null)
|
|
30523
|
+
.order('createdAt', { ascending: true })
|
|
30524
|
+
.limit(1);
|
|
30525
|
+
if (selectResult.error) {
|
|
30526
|
+
throw new DatabaseError(spaceTrim((block) => `
|
|
30527
|
+
|
|
30528
|
+
Error fetching agent "${agentNameOrPermanentId}" from Supabase:
|
|
30529
|
+
|
|
30530
|
+
${block(selectResult.error.message)}
|
|
30531
|
+
`));
|
|
30532
|
+
}
|
|
30533
|
+
const row = (_a = selectResult.data) === null || _a === void 0 ? void 0 : _a[0];
|
|
30534
|
+
return row ? this.mapAgentBasicInformationRow(row, isVerbose) : null;
|
|
30535
|
+
}
|
|
30536
|
+
/**
|
|
30537
|
+
* Converts one database row into public agent information.
|
|
30538
|
+
*
|
|
30539
|
+
* @param row - Database row carrying the persisted profile snapshot.
|
|
30540
|
+
* @param isVerbose - Whether profile-name mismatches should be logged.
|
|
30541
|
+
* @returns Agent profile with canonical database name and permanent id.
|
|
30542
|
+
*
|
|
30543
|
+
* @private internal helper of `AgentCollectionInSupabase`
|
|
30544
|
+
*/
|
|
30545
|
+
mapAgentBasicInformationRow({ agentName, agentProfile, permanentId }, isVerbose) {
|
|
30546
|
+
if (isVerbose && agentProfile.agentName !== agentName) {
|
|
30547
|
+
console.warn(spaceTrim(`
|
|
28904
30548
|
Agent name mismatch for agent "${agentName}". Using name from database.
|
|
28905
30549
|
|
|
28906
30550
|
agentName: "${agentName}"
|
|
28907
30551
|
agentProfile.agentName: "${agentProfile.agentName}"
|
|
28908
30552
|
`));
|
|
28909
|
-
|
|
28910
|
-
|
|
28911
|
-
|
|
28912
|
-
|
|
28913
|
-
|
|
28914
|
-
|
|
28915
|
-
});
|
|
30553
|
+
}
|
|
30554
|
+
return {
|
|
30555
|
+
...agentProfile,
|
|
30556
|
+
agentName,
|
|
30557
|
+
permanentId: permanentId || agentProfile.permanentId,
|
|
30558
|
+
};
|
|
28916
30559
|
}
|
|
28917
30560
|
/**
|
|
28918
30561
|
* Retrieves the permanent ID of an agent by its name or permanent ID.
|
|
@@ -28921,7 +30564,7 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱🚀] implements AgentCol
|
|
|
28921
30564
|
const selectResult = await this.supabaseClient
|
|
28922
30565
|
.from(this.getTableName('Agent'))
|
|
28923
30566
|
.select('permanentId')
|
|
28924
|
-
.or(
|
|
30567
|
+
.or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
|
|
28925
30568
|
.order('createdAt', { ascending: true }) // Pick oldest if multiple match by name
|
|
28926
30569
|
.limit(1);
|
|
28927
30570
|
if (selectResult.error || !selectResult.data || selectResult.data.length === 0) {
|
|
@@ -28936,7 +30579,7 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱🚀] implements AgentCol
|
|
|
28936
30579
|
const selectResult = await this.supabaseClient
|
|
28937
30580
|
.from(this.getTableName('Agent'))
|
|
28938
30581
|
.select('agentSource')
|
|
28939
|
-
.or(
|
|
30582
|
+
.or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
|
|
28940
30583
|
.is('deletedAt', null)
|
|
28941
30584
|
.order('createdAt', { ascending: true }) // Pick oldest if multiple match by name
|
|
28942
30585
|
.limit(1);
|
|
@@ -34417,7 +36060,8 @@ function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFull
|
|
|
34417
36060
|
* @private internal function of `$registeredLlmToolsMessage`
|
|
34418
36061
|
*/
|
|
34419
36062
|
function createUsedEnvMessage() {
|
|
34420
|
-
return `Unknown \`.env\` file`
|
|
36063
|
+
return `Unknown \`.env\` file`
|
|
36064
|
+
;
|
|
34421
36065
|
}
|
|
34422
36066
|
// TODO: [®] DRY Register logic
|
|
34423
36067
|
// TODO: [🧠][⚛] Maybe pass env as argument
|
|
@@ -35730,7 +37374,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
|
|
|
35730
37374
|
if (!executionTools || !executionTools.script) {
|
|
35731
37375
|
throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
|
|
35732
37376
|
}
|
|
35733
|
-
return Array.isArray(executionTools.script)
|
|
37377
|
+
return Array.isArray(executionTools.script)
|
|
37378
|
+
? executionTools.script
|
|
37379
|
+
: [executionTools.script];
|
|
35734
37380
|
}
|
|
35735
37381
|
/**
|
|
35736
37382
|
* Resolves the assistant-visible AgentKit tool response while preserving structured tool result data.
|
|
@@ -38792,7 +40438,7 @@ class OpenAiVectorStoreKnowledgeSourcePreparer {
|
|
|
38792
40438
|
return { skippedReason: 'invalid_data_url' };
|
|
38793
40439
|
}
|
|
38794
40440
|
return {
|
|
38795
|
-
file: new File([parsed.buffer], parsed.filename, {
|
|
40441
|
+
file: new File([new Uint8Array(parsed.buffer)], parsed.filename, {
|
|
38796
40442
|
type: parsed.mimeType,
|
|
38797
40443
|
}),
|
|
38798
40444
|
sizeBytes: parsed.buffer.length,
|
|
@@ -40555,7 +42201,7 @@ function emitAgentLlmExecutionToolsAssistantPreparationProgress(options) {
|
|
|
40555
42201
|
* Computes one stable hash from a JSON-serializable value.
|
|
40556
42202
|
*/
|
|
40557
42203
|
function computeJsonHash$1(value) {
|
|
40558
|
-
return SHA256(JSON.stringify(value)).toString();
|
|
42204
|
+
return CryptoJS.SHA256(JSON.stringify(value)).toString();
|
|
40559
42205
|
}
|
|
40560
42206
|
/**
|
|
40561
42207
|
* Handles OpenAI AgentKit-backed executions for `AgentLlmExecutionTools`.
|
|
@@ -40713,7 +42359,7 @@ AgentLlmExecutionToolsAgentKitRunner.vectorStoreCache = new Map();
|
|
|
40713
42359
|
* Computes one stable hash from a JSON-serializable value.
|
|
40714
42360
|
*/
|
|
40715
42361
|
function computeJsonHash(value) {
|
|
40716
|
-
return SHA256(JSON.stringify(value)).toString();
|
|
42362
|
+
return CryptoJS.SHA256(JSON.stringify(value)).toString();
|
|
40717
42363
|
}
|
|
40718
42364
|
/**
|
|
40719
42365
|
* Removes assistant-managed requirements before the prompt is executed via OpenAI Assistants.
|
|
@@ -41103,7 +42749,11 @@ function truncateAttachmentInlineText(content, maxCharacters) {
|
|
|
41103
42749
|
};
|
|
41104
42750
|
}
|
|
41105
42751
|
return {
|
|
41106
|
-
content:
|
|
42752
|
+
content: spaceTrim$1((block) => `
|
|
42753
|
+
${block(content.slice(0, Math.max(0, maxCharacters)))}
|
|
42754
|
+
|
|
42755
|
+
[...truncated...]
|
|
42756
|
+
`),
|
|
41107
42757
|
isTruncated: true,
|
|
41108
42758
|
};
|
|
41109
42759
|
}
|
|
@@ -41569,7 +43219,7 @@ class AgentLlmExecutionTools {
|
|
|
41569
43219
|
* Returns a virtual model name representing the agent behavior.
|
|
41570
43220
|
*/
|
|
41571
43221
|
get modelName() {
|
|
41572
|
-
const hash = SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
|
|
43222
|
+
const hash = CryptoJS.SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
|
|
41573
43223
|
const agentId = hash.substring(0, 10);
|
|
41574
43224
|
return (normalizeToKebabCase(this.title) + '-' + agentId);
|
|
41575
43225
|
}
|
|
@@ -45280,7 +46930,7 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
|
|
|
45280
46930
|
title: 'Anthropic Claude',
|
|
45281
46931
|
packageName: '@promptbook/anthropic-claude',
|
|
45282
46932
|
className: 'AnthropicClaudeExecutionTools',
|
|
45283
|
-
envVariables: ['ANTHROPIC_CLAUDE_API_KEY'],
|
|
46933
|
+
envVariables: ['ANTHROPIC_API_KEY', 'ANTHROPIC_CLAUDE_API_KEY'],
|
|
45284
46934
|
trustLevel: 'CLOSED',
|
|
45285
46935
|
order: MODEL_ORDERS.TOP_TIER,
|
|
45286
46936
|
getBoilerplateConfiguration() {
|
|
@@ -45297,14 +46947,17 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
|
|
|
45297
46947
|
};
|
|
45298
46948
|
},
|
|
45299
46949
|
createConfigurationFromEnv(env) {
|
|
46950
|
+
var _a;
|
|
45300
46951
|
// Note: Note using `process.env` BUT `env` to pass in the environment variables dynamically
|
|
45301
|
-
|
|
46952
|
+
// Note: `ANTHROPIC_API_KEY` takes precedence over the deprecated `ANTHROPIC_CLAUDE_API_KEY`
|
|
46953
|
+
const apiKey = (_a = env.ANTHROPIC_API_KEY) !== null && _a !== void 0 ? _a : env.ANTHROPIC_CLAUDE_API_KEY;
|
|
46954
|
+
if (typeof apiKey === 'string') {
|
|
45302
46955
|
return {
|
|
45303
46956
|
title: 'Claude (from env)',
|
|
45304
46957
|
packageName: '@promptbook/anthropic-claude',
|
|
45305
46958
|
className: 'AnthropicClaudeExecutionTools',
|
|
45306
46959
|
options: {
|
|
45307
|
-
apiKey
|
|
46960
|
+
apiKey,
|
|
45308
46961
|
},
|
|
45309
46962
|
};
|
|
45310
46963
|
}
|
|
@@ -48715,20 +50368,7 @@ function appendChatAttachmentContext(messageContent, attachments) {
|
|
|
48715
50368
|
}
|
|
48716
50369
|
|
|
48717
50370
|
/**
|
|
48718
|
-
*
|
|
48719
|
-
*
|
|
48720
|
-
* Used for "Adam" agent which is built in as default ancestor for new agents and other well known agents
|
|
48721
|
-
*
|
|
48722
|
-
* @public exported from `@promptbook/core`
|
|
48723
|
-
*/
|
|
48724
|
-
const CORE_AGENTS_SERVER = {
|
|
48725
|
-
title: 'Promptbook Core',
|
|
48726
|
-
description: `Core Promptbook server used for Adam agent which is built in as default ancestor for new agents and other well known agents.`,
|
|
48727
|
-
owner: PROMPTBOOK_LEGAL_ENTITY,
|
|
48728
|
-
url: 'https://core.ptbk.io/',
|
|
48729
|
-
};
|
|
48730
|
-
/**
|
|
48731
|
-
* Names of well known agents hosted on Core Promptbook server
|
|
50371
|
+
* Names of well known agents bundled in the local `.core` folder of every Agents Server
|
|
48732
50372
|
*
|
|
48733
50373
|
* - `Adam`: The default ancestor agent for new agents
|
|
48734
50374
|
* - `Teacher`: Agent that knows book syntax and can help with self-learning
|
|
@@ -48754,7 +50394,6 @@ const CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES = {
|
|
|
48754
50394
|
* @public exported from `@promptbook/core`
|
|
48755
50395
|
*/
|
|
48756
50396
|
const PUBLIC_AGENTS_SERVERS = [
|
|
48757
|
-
CORE_AGENTS_SERVER,
|
|
48758
50397
|
{
|
|
48759
50398
|
title: 'Promptbook Gallery',
|
|
48760
50399
|
description: `Gallery of ideas, AI professions,... like AI Agenta photobank.`,
|
|
@@ -49204,5 +50843,5 @@ function $generateBookBoilerplate(options) {
|
|
|
49204
50843
|
}
|
|
49205
50844
|
// TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
|
|
49206
50845
|
|
|
49207
|
-
export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AgentOsTranspiler, AnthropicClaudeManagedTranspiler, AnthropicClaudeSdkTranspiler, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, Book, CLAIM, CLI_APP_ID, COLOR_CONSTANTS,
|
|
50846
|
+
export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AgentOsTranspiler, AnthropicClaudeManagedTranspiler, AnthropicClaudeSdkTranspiler, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, Book, CLAIM, CLI_APP_ID, COLOR_CONSTANTS, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, ConflictError, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, DEFAULT_BOOK, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_CONCURRENT_UPLOADS, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, E2BTranspiler, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, HTTP_STATUS_CODES, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LIMITS, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NETWORK_LIMITS, NonTaskSectionTypes, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiAgentsTranspiler, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PROMPT_PARAMETER_SELF_LEARNING_ENABLED, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TIME_INTERVALS, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, VirusTotalFileSecurityChecker, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, appendChatAttachmentContext, appendChatAttachmentContextWithContent, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, createStandaloneBookLanguageMarkdown, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, formatChatAttachmentContentContext, formatChatAttachmentContext, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getAllCommitmentsToolTitles, getCommitmentDefinition, getGroupedCommitmentDefinitions, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, normalizeChatAttachments, padBook, parseAgentSource, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, resolveChatAttachmentContents, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
|
|
49208
50847
|
//# sourceMappingURL=index.es.js.map
|