This commit is contained in:
parent
05c8a39bd8
commit
c40c5d97db
74 changed files with 863 additions and 669 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { appConfig } from '../config';
|
||||
import { appConfig, type GardenAppConfig } from '../config';
|
||||
import { AGENT_FLOAT_COUNT, writeAgentValues } from '../pipelines/agents/agent-limits';
|
||||
import { clamp, easeOutQuad, mix, mixAngle, smoothstep } from '../utils/math';
|
||||
|
||||
|
|
@ -18,8 +18,11 @@ interface IntroTitleAgentOptions {
|
|||
}
|
||||
|
||||
type RandomSource = () => number;
|
||||
type IntroPathEasing = GardenAppConfig['simulation']['intro']['pathEasing'];
|
||||
|
||||
const INTRO_TITLE = appConfig.simulation.intro.title;
|
||||
const isLinearPathEasing = (pathEasing: IntroPathEasing): boolean =>
|
||||
pathEasing === 'linear';
|
||||
|
||||
export const createIntroTitleAgents = ({
|
||||
count,
|
||||
|
|
@ -169,16 +172,22 @@ const createIntroTitlePoints = (
|
|||
width: number,
|
||||
height: number
|
||||
): Array<IntroTitlePoint> => {
|
||||
const safeMaxPixels = Math.max(1, appConfig.simulation.intro.maskMaxPixels);
|
||||
const maskScale = Math.min(1, Math.sqrt(safeMaxPixels / Math.max(1, width * height)));
|
||||
const maskWidth = Math.max(1, Math.round(width * maskScale));
|
||||
const maskHeight = Math.max(1, Math.round(height * maskScale));
|
||||
const pointScaleX = width / maskWidth;
|
||||
const pointScaleY = height / maskHeight;
|
||||
const maskCanvas = document.createElement('canvas');
|
||||
maskCanvas.width = width;
|
||||
maskCanvas.height = height;
|
||||
maskCanvas.width = maskWidth;
|
||||
maskCanvas.height = maskHeight;
|
||||
const context = maskCanvas.getContext('2d', { willReadFrequently: true });
|
||||
if (!context) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const fontSize = getIntroTitleFontSize(context, width, height);
|
||||
context.clearRect(0, 0, width, height);
|
||||
const fontSize = getIntroTitleFontSize(context, maskWidth, maskHeight);
|
||||
context.clearRect(0, 0, maskWidth, maskHeight);
|
||||
context.font = `${fontSize}px ${appConfig.simulation.intro.fontFamily}`;
|
||||
context.textAlign = 'center';
|
||||
context.textBaseline = 'middle';
|
||||
|
|
@ -192,42 +201,44 @@ const createIntroTitlePoints = (
|
|||
const letterSpacing = fontSize * appConfig.simulation.intro.letterSpacingEm;
|
||||
drawIntroTitleText(
|
||||
context,
|
||||
width / 2,
|
||||
height * appConfig.simulation.intro.verticalAnchor,
|
||||
maskWidth / 2,
|
||||
maskHeight * appConfig.simulation.intro.verticalAnchor,
|
||||
letterSpacing,
|
||||
'stroke'
|
||||
);
|
||||
drawIntroTitleText(
|
||||
context,
|
||||
width / 2,
|
||||
height * appConfig.simulation.intro.verticalAnchor,
|
||||
maskWidth / 2,
|
||||
maskHeight * appConfig.simulation.intro.verticalAnchor,
|
||||
letterSpacing,
|
||||
'fill'
|
||||
);
|
||||
|
||||
const { data } = context.getImageData(0, 0, width, height);
|
||||
const { data } = context.getImageData(0, 0, maskWidth, maskHeight);
|
||||
const step = Math.max(
|
||||
1,
|
||||
Math.floor(Math.min(width, height) / appConfig.simulation.intro.maskSampleDensity)
|
||||
Math.floor(
|
||||
Math.min(maskWidth, maskHeight) / appConfig.simulation.intro.maskSampleDensity
|
||||
)
|
||||
);
|
||||
const points: Array<IntroTitlePoint> = [];
|
||||
const characterColorBoundaries = getIntroTitleColorBoundaries(
|
||||
context,
|
||||
width,
|
||||
maskWidth,
|
||||
letterSpacing
|
||||
);
|
||||
|
||||
for (let y = 0; y < height; y += step) {
|
||||
for (let x = 0; x < width; x += step) {
|
||||
const alpha = getMaskAlpha(data, width, height, x, y);
|
||||
for (let y = 0; y < maskHeight; y += step) {
|
||||
for (let x = 0; x < maskWidth; x += step) {
|
||||
const alpha = getMaskAlpha(data, maskWidth, maskHeight, x, y);
|
||||
if (alpha < appConfig.simulation.intro.maskAlphaThreshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
points.push({
|
||||
x,
|
||||
y,
|
||||
tangent: estimateMaskTangent(data, width, height, x, y),
|
||||
x: x * pointScaleX,
|
||||
y: y * pointScaleY,
|
||||
tangent: estimateMaskTangent(data, maskWidth, maskHeight, x, y),
|
||||
colorIndex: getIntroTitleColorIndex(x, characterColorBoundaries),
|
||||
});
|
||||
}
|
||||
|
|
@ -244,8 +255,10 @@ const getIntroTitleColorBoundaries = (
|
|||
const letters = Array.from(INTRO_TITLE);
|
||||
const totalWidth = measureIntroTitleText(context, letters, letterSpacing);
|
||||
let x = width / 2 - totalWidth / 2;
|
||||
const [firstCutLetter, secondCutLetter] =
|
||||
appConfig.simulation.intro.titleColorCutLetters;
|
||||
const cutLetters = appConfig.simulation.intro.titleColorCutLetters
|
||||
.map((cutLetter) => Math.min(letters.length - 1, Math.max(1, Math.round(cutLetter))))
|
||||
.sort((a, b) => a - b);
|
||||
const [firstCutLetter, secondCutLetter] = cutLetters;
|
||||
const letterBoxes = letters.map((letter, index) => {
|
||||
const letterWidth = context.measureText(letter).width;
|
||||
const box = {
|
||||
|
|
@ -401,7 +414,7 @@ const createSeededRandom = (seed: number): RandomSource => {
|
|||
};
|
||||
|
||||
const easePathProgress = (amount: number): number => {
|
||||
if (appConfig.simulation.intro.pathEasing === 'linear') {
|
||||
if (isLinearPathEasing(appConfig.simulation.intro.pathEasing)) {
|
||||
return amount;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue