Compare commits

...

5 Commits

Author SHA1 Message Date
release bot
2e275adbb6 📦 2.0.0-rc.3 2022-04-12 21:06:46 +00:00
platane
66fef03781 👷 2022-04-12 22:04:20 +02:00
platane
5841a21a09 👷 remove benchmark test 2022-04-12 22:01:29 +02:00
platane
cce5c4514d ♻️ refacto: rename options 2022-04-12 22:01:29 +02:00
platane
fb82d42d53 🚀 Allow to pass option as Json 2022-04-12 21:34:25 +02:00
19 changed files with 242 additions and 118 deletions

View File

@@ -18,19 +18,6 @@ jobs:
- run: yarn lint
- run: yarn test --ci
test-benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
cache: yarn
node-version: 16
- run: yarn install --frozen-lockfile
- run: ( cd packages/gif-creator ; yarn benchmark )
test-action:
runs-on: ubuntu-latest
steps:
@@ -79,7 +66,7 @@ jobs:
GITHUB_USER_CONTRIBUTION_API_ENDPOINT: https://snk-one.vercel.app/api/github-user-contribution/
- uses: crazy-max/ghaction-github-pages@v2.6.0
if: success() && github.ref.name == 'main'
if: success() && github.ref == 'refs/heads/main'
with:
target_branch: gh-pages
build_dir: packages/demo/dist

View File

@@ -4,7 +4,7 @@ author: "platane"
runs:
using: docker
image: docker://platane/snk@sha256:339ea30dd50fc9566cd9b7244729b5ea12f14afea44585770212b96d3389748b
image: docker://platane/snk@sha256:e40bb02de6ed0f164eca8586b3f6c32109b2bcb426cd57c6882764825b40fe0d
inputs:
github_user_name:

View File

@@ -1,7 +1,7 @@
{
"name": "snk",
"description": "Generates a snake game from a github user contributions grid",
"version": "2.0.0-rc.2",
"version": "2.0.0-rc.3",
"private": true,
"repository": "github:platane/snk",
"devDependencies": {

View File

@@ -1,17 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should parse /out.svg?color_snake=orange&color_dots=#bfd6f6,#8dbdff,#64a1f4,#4b91f1,#3c7dd9 1`] = `
exports[`should parse /out.svg?{"color_snake":"yellow","color_dots":["#000","#111","#222","#333","#444"]} 1`] = `
Object {
"animationOptions": Object {
"frameDuration": 100,
"step": 1,
},
"drawOptions": Object {
"colorDotBorder": "#1b1f230a",
"colorDots": Array [
"#bfd6f6",
"#8dbdff",
"#64a1f4",
"#4b91f1",
"#3c7dd9",
"#000",
"#111",
"#222",
"#333",
"#444",
],
"colorEmpty": "#bfd6f6",
"colorEmpty": "#000",
"colorSnake": "yellow",
"dark": undefined,
"sizeCell": 16,
"sizeDot": 12,
"sizeDotBorderRadius": 2,
},
"filename": "/out.svg",
"format": "svg",
}
`;
exports[`should parse /out.svg?color_snake=orange&color_dots=#000,#111,#222,#333,#444 1`] = `
Object {
"animationOptions": Object {
"frameDuration": 100,
"step": 1,
},
"drawOptions": Object {
"colorDotBorder": "#1b1f230a",
"colorDots": Array [
"#000",
"#111",
"#222",
"#333",
"#444",
],
"colorEmpty": "#000",
"colorSnake": "orange",
"dark": undefined,
"sizeCell": 16,
@@ -20,15 +51,51 @@ Object {
},
"filename": "/out.svg",
"format": "svg",
"gifOptions": Object {
}
`;
exports[`should parse /out.svg?color_snake=orange&color_dots=#000,#111,#222,#333,#444&dark_color_dots=#a00,#a11,#a22,#a33,#a44 1`] = `
Object {
"animationOptions": Object {
"frameDuration": 100,
"step": 1,
},
"drawOptions": Object {
"colorDotBorder": "#1b1f230a",
"colorDots": Array [
"#000",
"#111",
"#222",
"#333",
"#444",
],
"colorEmpty": "#000",
"colorSnake": "orange",
"dark": Object {
"colorDots": Array [
"#a00",
"#a11",
"#a22",
"#a33",
"#a44",
],
"colorEmpty": "#a00",
},
"sizeCell": 16,
"sizeDot": 12,
"sizeDotBorderRadius": 2,
},
"filename": "/out.svg",
"format": "svg",
}
`;
exports[`should parse path/to/out.gif 1`] = `
Object {
"animationOptions": Object {
"frameDuration": 100,
"step": 1,
},
"drawOptions": Object {
"colorDotBorder": "#1b1f230a",
"colorDots": Array [
@@ -58,9 +125,5 @@ Object {
},
"filename": "path/to/out.gif",
"format": "gif",
"gifOptions": Object {
"frameDuration": 100,
"step": 1,
},
}
`;

View File

@@ -3,7 +3,11 @@ import { parseEntry } from "../outputsOptions";
[
"path/to/out.gif",
"/out.svg?color_snake=orange&color_dots=#bfd6f6,#8dbdff,#64a1f4,#4b91f1,#3c7dd9",
"/out.svg?color_snake=orange&color_dots=#000,#111,#222,#333,#444",
`/out.svg?{"color_snake":"yellow","color_dots":["#000","#111","#222","#333","#444"]}`,
"/out.svg?color_snake=orange&color_dots=#000,#111,#222,#333,#444&dark_color_dots=#a00,#a11,#a22,#a33,#a44",
].forEach((entry) =>
it(`should parse ${entry}`, () => {
expect(parseEntry(entry)).toMatchSnapshot();

View File

@@ -3,17 +3,15 @@ import { userContributionToGrid } from "./userContributionToGrid";
import { getBestRoute } from "@snk/solver/getBestRoute";
import { snake4 } from "@snk/types/__fixtures__/snake";
import { getPathToPose } from "@snk/solver/getPathToPose";
import { Options as DrawOptions } from "@snk/svg-creator";
import type { DrawOptions as DrawOptions } from "@snk/svg-creator";
import type { AnimationOptions } from "@snk/gif-creator";
export const generateContributionSnake = async (
userName: string,
outputs: ({
format: "svg" | "gif";
drawOptions: DrawOptions;
gifOptions: {
frameDuration: number;
step: number;
};
animationOptions: AnimationOptions;
} | null)[]
) => {
console.log("🎣 fetching github user contribution");
@@ -29,17 +27,23 @@ export const generateContributionSnake = async (
return Promise.all(
outputs.map(async (out, i) => {
if (!out) return;
const { format, drawOptions, gifOptions } = out;
const { format, drawOptions, animationOptions } = out;
switch (format) {
case "svg": {
console.log(`🖌 creating svg (outputs[${i}])`);
const { createSvg } = await import("@snk/svg-creator");
return createSvg(grid, chain, drawOptions, gifOptions);
return createSvg(grid, cells, chain, drawOptions, animationOptions);
}
case "gif": {
console.log(`📹 creating gif (outputs[${i}])`);
const { createGif } = await import("@snk/gif-creator");
return await createGif(grid, chain, drawOptions, gifOptions);
return await createGif(
grid,
cells,
chain,
drawOptions,
animationOptions
);
}
}
})

View File

@@ -1,15 +1,28 @@
import { Options as DrawOptions } from "@snk/svg-creator";
import type { AnimationOptions } from "@snk/gif-creator";
import type { DrawOptions as DrawOptions } from "@snk/svg-creator";
import { palettes } from "./palettes";
export const parseOutputsOption = (lines: string[]) => lines.map(parseEntry);
export const parseEntry = (entry: string) => {
const m = entry.trim().match(/^(.+\.(svg|gif))(\?.*)?$/);
const m = entry.trim().match(/^(.+\.(svg|gif))(\?(.*))?$/);
if (!m) return null;
const [_, filename, format, query] = m;
const [, filename, format, , query] = m;
const sp = new URLSearchParams(query || "");
let sp = new URLSearchParams(query || "");
try {
const o = JSON.parse(query);
if (Array.isArray(o.color_dots)) o.color_dots = o.color_dots.join(",");
if (Array.isArray(o.dark_color_dots))
o.dark_color_dots = o.dark_color_dots.join(",");
sp = new URLSearchParams(o);
} catch (err) {
if (!(err instanceof SyntaxError)) throw err;
}
const drawOptions: DrawOptions = {
sizeDotBorderRadius: 2,
@@ -17,7 +30,7 @@ export const parseEntry = (entry: string) => {
sizeDot: 12,
...palettes["default"],
};
const gifOptions = { step: 1, frameDuration: 100 };
const animationOptions: AnimationOptions = { step: 1, frameDuration: 100 };
{
const palette = palettes[sp.get("palette")!];
@@ -50,5 +63,10 @@ export const parseEntry = (entry: string) => {
if (sp.has("dark_color_snake") && drawOptions.dark)
drawOptions.dark.colorSnake = sp.get("color_snake")!;
return { filename, format: format as "svg" | "gif", drawOptions, gifOptions };
return {
filename,
format: format as "svg" | "gif",
drawOptions,
animationOptions,
};
};

View File

@@ -1,4 +1,4 @@
import { Options as DrawOptions } from "@snk/svg-creator";
import { DrawOptions as DrawOptions } from "@snk/svg-creator";
export const palettes: Record<
string,

View File

@@ -1,7 +1,7 @@
import { Color, Grid } from "@snk/types/grid";
import { drawLerpWorld, drawWorld } from "@snk/draw/drawWorld";
import { Snake } from "@snk/types/snake";
import type { Options as DrawOptions } from "@snk/svg-creator";
import type { DrawOptions as DrawOptions } from "@snk/svg-creator";
export const drawOptions: DrawOptions = {
sizeDotBorderRadius: 2,
@@ -68,7 +68,7 @@ export const createCanvas = ({
const draw = (grid: Grid, snake: Snake, stack: Color[]) => {
ctx.clearRect(0, 0, 9999, 9999);
drawWorld(ctx, grid, snake, stack, drawOptions);
drawWorld(ctx, grid, null, snake, stack, drawOptions);
};
const drawLerp = (
@@ -79,7 +79,7 @@ export const createCanvas = ({
k: number
) => {
ctx.clearRect(0, 0, 9999, 9999);
drawLerpWorld(ctx, grid, snake0, snake1, stack, k, drawOptions);
drawLerpWorld(ctx, grid, null, snake0, snake1, stack, k, drawOptions);
};
const highlightCell = (x: number, y: number, color = "orange") => {

View File

@@ -3,6 +3,7 @@ import { step } from "@snk/solver/step";
import { isStableAndBound, stepSpring } from "./springUtils";
import type { Res } from "@snk/github-user-contribution";
import type { Snake } from "@snk/types/snake";
import type { Point } from "@snk/types/point";
import {
drawLerpWorld,
getCanvasWorldSize,
@@ -12,6 +13,7 @@ import { userContributionToGrid } from "@snk/action/userContributionToGrid";
import { createSvg } from "@snk/svg-creator";
import { createRpcClient } from "./worker-utils";
import type { API as WorkerAPI } from "./demo.interactive.worker";
import { AnimationOptions } from "@snk/gif-creator";
const createForm = ({
onSubmit,
@@ -116,10 +118,12 @@ const createGithubProfile = () => {
const createViewer = ({
grid0,
chain,
cells,
drawOptions,
}: {
grid0: Grid;
chain: Snake[];
cells: Point[];
drawOptions: DrawOptions;
}) => {
//
@@ -159,7 +163,7 @@ const createViewer = ({
const k = spring.x % 1;
ctx.clearRect(0, 0, 9999, 9999);
drawLerpWorld(ctx, grid, snake0, snake1, stack, k, drawOptions);
drawLerpWorld(ctx, grid, null, snake0, snake1, stack, k, drawOptions);
if (!stable) animationFrame = requestAnimationFrame(loop);
};
@@ -189,9 +193,9 @@ const createViewer = ({
//
// svg
const svgLink = document.createElement("a");
const svgString = createSvg(grid0, chain, drawOptions, {
const svgString = createSvg(grid0, cells, chain, drawOptions, {
frameDuration: 100,
});
} as AnimationOptions);
const svgImageUri = `data:image/*;charset=utf-8;base64,${btoa(svgString)}`;
svgLink.href = svgImageUri;
svgLink.innerText = "github-user-contribution.svg";
@@ -237,7 +241,6 @@ const onSubmit = async (userName: string) => {
colorDots: colorScheme as any,
colorEmpty: colorScheme[0],
colorSnake: "purple",
cells,
};
const grid = userContributionToGrid(cells, colorScheme);
@@ -246,7 +249,7 @@ const onSubmit = async (userName: string) => {
dispose();
createViewer({ grid0: grid, chain, drawOptions });
createViewer({ grid0: grid, chain, cells, drawOptions });
};
const worker = new Worker(

View File

@@ -4,12 +4,15 @@ import { createSvg } from "@snk/svg-creator";
import { grid, snake } from "./sample";
import { drawOptions } from "./canvas";
import { getPathToPose } from "@snk/solver/getPathToPose";
import type { AnimationOptions } from "@snk/gif-creator";
const chain = getBestRoute(grid, snake);
chain.push(...getPathToPose(chain.slice(-1)[0], snake)!);
(async () => {
const svg = await createSvg(grid, chain, drawOptions, { frameDuration: 200 });
const svg = await createSvg(grid, null, chain, drawOptions, {
frameDuration: 200,
} as AnimationOptions);
const container = document.createElement("div");
container.innerHTML = svg;

View File

@@ -10,17 +10,17 @@ type Options = {
sizeCell: number;
sizeDot: number;
sizeDotBorderRadius: number;
cells?: Point[];
};
export const drawGrid = (
ctx: CanvasRenderingContext2D,
grid: Grid,
cells: Point[] | null,
o: Options
) => {
for (let x = grid.width; x--; )
for (let y = grid.height; y--; ) {
if (!o.cells || o.cells.some((c) => c.x === x && c.y === y)) {
if (!cells || cells.some((c) => c.x === x && c.y === y)) {
const c = getColor(grid, x, y);
// @ts-ignore
const color = !c ? o.colorEmpty : o.colorDots[c];

View File

@@ -1,8 +1,8 @@
import { drawGrid } from "./drawGrid";
import { drawSnake, drawSnakeLerp } from "./drawSnake";
import type { Grid, Color } from "@snk/types/grid";
import type { Point } from "@snk/types/point";
import type { Snake } from "@snk/types/snake";
import type { Point } from "@snk/types/point";
export type Options = {
colorDots: Record<Color, string>;
@@ -12,7 +12,6 @@ export type Options = {
sizeCell: number;
sizeDot: number;
sizeDotBorderRadius: number;
cells?: Point[];
};
export const drawStack = (
@@ -37,6 +36,7 @@ export const drawStack = (
export const drawWorld = (
ctx: CanvasRenderingContext2D,
grid: Grid,
cells: Point[] | null,
snake: Snake,
stack: Color[],
o: Options
@@ -44,7 +44,7 @@ export const drawWorld = (
ctx.save();
ctx.translate(1 * o.sizeCell, 2 * o.sizeCell);
drawGrid(ctx, grid, o);
drawGrid(ctx, grid, cells, o);
drawSnake(ctx, snake, o);
ctx.restore();
@@ -68,6 +68,7 @@ export const drawWorld = (
export const drawLerpWorld = (
ctx: CanvasRenderingContext2D,
grid: Grid,
cells: Point[] | null,
snake0: Snake,
snake1: Snake,
stack: Color[],
@@ -77,7 +78,7 @@ export const drawLerpWorld = (
ctx.save();
ctx.translate(1 * o.sizeCell, 2 * o.sizeCell);
drawGrid(ctx, grid, o);
drawGrid(ctx, grid, cells, o);
drawSnakeLerp(ctx, snake0, snake1, k, o);
ctx.translate(0, (grid.height + 2) * o.sizeCell);

View File

@@ -2,7 +2,7 @@ import * as fs from "fs";
import { performance } from "perf_hooks";
import { createSnakeFromCells } from "@snk/types/snake";
import { realistic as grid } from "@snk/types/__fixtures__/grid";
import { createGif } from "..";
import { AnimationOptions, createGif } from "..";
import { getBestRoute } from "@snk/solver/getBestRoute";
import { getPathToPose } from "@snk/solver/getPathToPose";
import type { Options as DrawOptions } from "@snk/draw/drawWorld";
@@ -35,7 +35,7 @@ const drawOptions: DrawOptions = {
colorSnake: "purple",
};
const gifOptions = { frameDuration: 100, step: 1 };
const animationOptions: AnimationOptions = { frameDuration: 100, step: 1 };
(async () => {
for (
@@ -50,7 +50,13 @@ const gifOptions = { frameDuration: 100, step: 1 };
const chainL = chain.slice(0, length);
for (let k = 0; k < 10 && (Date.now() - start < 10 * 1000 || k < 2); k++) {
const s = performance.now();
buffer = await createGif(grid, chainL, drawOptions, gifOptions);
buffer = await createGif(
grid,
null,
chainL,
drawOptions,
animationOptions
);
stats.push(performance.now() - s);
}

View File

@@ -1,6 +1,6 @@
import * as fs from "fs";
import * as path from "path";
import { createGif } from "..";
import { AnimationOptions, createGif } from "..";
import * as grids from "@snk/types/__fixtures__/grid";
import { snake3 as snake } from "@snk/types/__fixtures__/snake";
import { createSnakeFromCells, nextSnake } from "@snk/types/snake";
@@ -20,7 +20,7 @@ const drawOptions: DrawOptions = {
colorSnake: "purple",
};
const gifOptions = { frameDuration: 200, step: 1 };
const animationOptions: AnimationOptions = { frameDuration: 200, step: 1 };
const dir = path.resolve(__dirname, "__snapshots__");
@@ -40,7 +40,13 @@ for (const key of [
const chain = [snake, ...getBestRoute(grid, snake)!];
const gif = await createGif(grid, chain, drawOptions, gifOptions);
const gif = await createGif(
grid,
null,
chain,
drawOptions,
animationOptions
);
expect(gif).toBeDefined();
@@ -64,7 +70,7 @@ it(`should generate swipper`, async () => {
}
}
const gif = await createGif(grid, chain, drawOptions, gifOptions);
const gif = await createGif(grid, null, chain, drawOptions, animationOptions);
expect(gif).toBeDefined();

View File

@@ -5,10 +5,11 @@ import { createCanvas } from "canvas";
import { Grid, copyGrid, Color } from "@snk/types/grid";
import { Snake } from "@snk/types/snake";
import {
Options,
Options as DrawOptions,
drawLerpWorld,
getCanvasWorldSize,
} from "@snk/draw/drawWorld";
import type { Point } from "@snk/types/point";
import { step } from "@snk/solver/step";
import tmp from "tmp";
import gifsicle from "gifsicle";
@@ -29,11 +30,14 @@ const withTmpDir = async <T>(
}
};
export type AnimationOptions = { frameDuration: number; step: number };
export const createGif = async (
grid0: Grid,
cells: Point[] | null,
chain: Snake[],
drawOptions: Options,
gifOptions: { frameDuration: number; step: number }
drawOptions: DrawOptions,
animationOptions: AnimationOptions
) =>
withTmpDir(async (dir) => {
const { width, height } = getCanvasWorldSize(grid0, drawOptions);
@@ -46,7 +50,7 @@ export const createGif = async (
const encoder = new GIFEncoder(width, height, "neuquant", true);
encoder.setRepeat(0);
encoder.setDelay(gifOptions.frameDuration);
encoder.setDelay(animationOptions.frameDuration);
encoder.start();
for (let i = 0; i < chain.length; i += 1) {
@@ -54,17 +58,18 @@ export const createGif = async (
const snake1 = chain[Math.min(chain.length - 1, i + 1)];
step(grid, stack, snake0);
for (let k = 0; k < gifOptions.step; k++) {
for (let k = 0; k < animationOptions.step; k++) {
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
drawLerpWorld(
ctx,
grid,
cells,
snake0,
snake1,
stack,
k / gifOptions.step,
k / animationOptions.step,
drawOptions
);

View File

@@ -1,11 +1,12 @@
import * as fs from "fs";
import * as path from "path";
import { createSvg, Options } from "..";
import { createSvg, DrawOptions as DrawOptions } from "..";
import * as grids from "@snk/types/__fixtures__/grid";
import { snake3 as snake } from "@snk/types/__fixtures__/snake";
import { getBestRoute } from "@snk/solver/getBestRoute";
import { AnimationOptions } from "@snk/gif-creator";
const drawOptions: Options = {
const drawOptions: DrawOptions = {
sizeDotBorderRadius: 2,
sizeCell: 16,
sizeDot: 12,
@@ -19,7 +20,7 @@ const drawOptions: Options = {
},
};
const gifOptions = { frameDuration: 100, step: 1 };
const animationOptions: AnimationOptions = { frameDuration: 100, step: 1 };
const dir = path.resolve(__dirname, "__snapshots__");
@@ -31,7 +32,13 @@ for (const [key, grid] of Object.entries(grids))
it(`should generate ${key} svg`, async () => {
const chain = [snake, ...getBestRoute(grid, snake)!];
const svg = await createSvg(grid, chain, drawOptions, gifOptions);
const svg = await createSvg(
grid,
null,
chain,
drawOptions,
animationOptions
);
expect(svg).toBeDefined();

View File

@@ -14,8 +14,9 @@ import { createGrid } from "./grid";
import { createStack } from "./stack";
import { h } from "./utils";
import * as csso from "csso";
import { AnimationOptions } from "@snk/gif-creator";
export type Options = {
export type DrawOptions = {
colorDots: Record<Color, string>;
colorEmpty: string;
colorDotBorder: string;
@@ -23,7 +24,6 @@ export type Options = {
sizeCell: number;
sizeDot: number;
sizeDotBorderRadius: number;
cells?: Point[];
dark?: {
colorDots: Record<Color, string>;
colorEmpty: string;
@@ -40,12 +40,12 @@ const getCellsFromGrid = ({ width, height }: Grid) =>
const createLivingCells = (
grid0: Grid,
chain: Snake[],
drawOptions: Options
cells: Point[] | null
) => {
const cells: (Point & {
const livingCells: (Point & {
t: number | null;
color: Color | Empty;
})[] = (drawOptions.cells ?? getCellsFromGrid(grid0)).map(({ x, y }) => ({
})[] = (cells ?? getCellsFromGrid(grid0)).map(({ x, y }) => ({
x,
y,
t: null,
@@ -60,31 +60,32 @@ const createLivingCells = (
if (isInside(grid, x, y) && !isEmpty(getColor(grid, x, y))) {
setColorEmpty(grid, x, y);
const cell = cells.find((c) => c.x === x && c.y === y)!;
const cell = livingCells.find((c) => c.x === x && c.y === y)!;
cell.t = i / chain.length;
}
}
return cells;
return livingCells;
};
export const createSvg = (
grid: Grid,
cells: Point[] | null,
chain: Snake[],
drawOptions: Options,
gifOptions: { frameDuration: number }
drawOptions: DrawOptions,
animationOptions: Pick<AnimationOptions, "frameDuration">
) => {
const width = (grid.width + 2) * drawOptions.sizeCell;
const height = (grid.height + 5) * drawOptions.sizeCell;
const duration = gifOptions.frameDuration * chain.length;
const duration = animationOptions.frameDuration * chain.length;
const cells = createLivingCells(grid, chain, drawOptions);
const livingCells = createLivingCells(grid, chain, cells);
const elements = [
createGrid(cells, drawOptions, duration),
createGrid(livingCells, drawOptions, duration),
createStack(
cells,
livingCells,
drawOptions,
grid.width * drawOptions.sizeCell,
(grid.height + 2) * drawOptions.sizeCell,
@@ -134,7 +135,7 @@ export const createSvg = (
const optimizeCss = (css: string) => csso.minify(css).css;
const optimizeSvg = (svg: string) => svg;
const generateColorVar = (drawOptions: Options) =>
const generateColorVar = (drawOptions: DrawOptions) =>
`
:root {
--cb: ${drawOptions.colorDotBorder};

View File

@@ -32796,13 +32796,13 @@ var generateContributionSnake = function (userName, outputs) { return __awaiter(
chain = (0, getBestRoute_1.getBestRoute)(grid, snake);
chain.push.apply(chain, (0, getPathToPose_1.getPathToPose)(chain.slice(-1)[0], snake));
return [2 /*return*/, Promise.all(outputs.map(function (out, i) { return __awaiter(void 0, void 0, void 0, function () {
var format, drawOptions, gifOptions, _a, createSvg, createGif;
var format, drawOptions, animationOptions, _a, createSvg, createGif;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (!out)
return [2 /*return*/];
format = out.format, drawOptions = out.drawOptions, gifOptions = out.gifOptions;
format = out.format, drawOptions = out.drawOptions, animationOptions = out.animationOptions;
_a = format;
switch (_a) {
case "svg": return [3 /*break*/, 1];
@@ -32814,13 +32814,13 @@ var generateContributionSnake = function (userName, outputs) { return __awaiter(
return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(__webpack_require__(7415)); })];
case 2:
createSvg = (_b.sent()).createSvg;
return [2 /*return*/, createSvg(grid, chain, drawOptions, gifOptions)];
return [2 /*return*/, createSvg(grid, cells, chain, drawOptions, animationOptions)];
case 3:
console.log("\uD83D\uDCF9 creating gif (outputs[".concat(i, "])"));
return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(__webpack_require__(340)); })];
case 4:
createGif = (_b.sent()).createGif;
return [4 /*yield*/, createGif(grid, chain, drawOptions, gifOptions)];
return [4 /*yield*/, createGif(grid, cells, chain, drawOptions, animationOptions)];
case 5: return [2 /*return*/, _b.sent()];
case 6: return [2 /*return*/];
}
@@ -32962,13 +32962,25 @@ var palettes_1 = __webpack_require__(3848);
var parseOutputsOption = function (lines) { return lines.map(exports.parseEntry); };
exports.parseOutputsOption = parseOutputsOption;
var parseEntry = function (entry) {
var m = entry.trim().match(/^(.+\.(svg|gif))(\?.*)?$/);
var m = entry.trim().match(/^(.+\.(svg|gif))(\?(.*))?$/);
if (!m)
return null;
var _ = m[0], filename = m[1], format = m[2], query = m[3];
var filename = m[1], format = m[2], query = m[4];
var sp = new URLSearchParams(query || "");
try {
var o = JSON.parse(query);
if (Array.isArray(o.color_dots))
o.color_dots = o.color_dots.join(",");
if (Array.isArray(o.dark_color_dots))
o.dark_color_dots = o.dark_color_dots.join(",");
sp = new URLSearchParams(o);
}
catch (err) {
if (!(err instanceof SyntaxError))
throw err;
}
var drawOptions = __assign({ sizeDotBorderRadius: 2, sizeCell: 16, sizeDot: 12 }, palettes_1.palettes["default"]);
var gifOptions = { step: 1, frameDuration: 100 };
var animationOptions = { step: 1, frameDuration: 100 };
{
var palette = palettes_1.palettes[sp.get("palette")];
if (palette) {
@@ -32994,7 +33006,12 @@ var parseEntry = function (entry) {
drawOptions.dark.colorDotBorder = sp.get("color_dot_border");
if (sp.has("dark_color_snake") && drawOptions.dark)
drawOptions.dark.colorSnake = sp.get("color_snake");
return { filename: filename, format: format, drawOptions: drawOptions, gifOptions: gifOptions };
return {
filename: filename,
format: format,
drawOptions: drawOptions,
animationOptions: animationOptions
};
};
exports.parseEntry = parseEntry;
@@ -33085,10 +33102,10 @@ exports.__esModule = true;
exports.drawGrid = void 0;
var grid_1 = __webpack_require__(2881);
var pathRoundedRect_1 = __webpack_require__(2356);
var drawGrid = function (ctx, grid, o) {
var drawGrid = function (ctx, grid, cells, o) {
var _loop_1 = function (x) {
var _loop_2 = function (y) {
if (!o.cells || o.cells.some(function (c) { return c.x === x && c.y === y; })) {
if (!cells || cells.some(function (c) { return c.x === x && c.y === y; })) {
var c = (0, grid_1.getColor)(grid, x, y);
// @ts-ignore
var color = !c ? o.colorEmpty : o.colorDots[c];
@@ -33186,10 +33203,10 @@ var drawStack = function (ctx, stack, max, width, o) {
ctx.restore();
};
exports.drawStack = drawStack;
var drawWorld = function (ctx, grid, snake, stack, o) {
var drawWorld = function (ctx, grid, cells, snake, stack, o) {
ctx.save();
ctx.translate(1 * o.sizeCell, 2 * o.sizeCell);
(0, drawGrid_1.drawGrid)(ctx, grid, o);
(0, drawGrid_1.drawGrid)(ctx, grid, cells, o);
(0, drawSnake_1.drawSnake)(ctx, snake, o);
ctx.restore();
ctx.save();
@@ -33204,10 +33221,10 @@ var drawWorld = function (ctx, grid, snake, stack, o) {
// ctx.restore();
};
exports.drawWorld = drawWorld;
var drawLerpWorld = function (ctx, grid, snake0, snake1, stack, k, o) {
var drawLerpWorld = function (ctx, grid, cells, snake0, snake1, stack, k, o) {
ctx.save();
ctx.translate(1 * o.sizeCell, 2 * o.sizeCell);
(0, drawGrid_1.drawGrid)(ctx, grid, o);
(0, drawGrid_1.drawGrid)(ctx, grid, cells, o);
(0, drawSnake_1.drawSnakeLerp)(ctx, snake0, snake1, k, o);
ctx.translate(0, (grid.height + 2) * o.sizeCell);
var max = grid.data.reduce(function (sum, x) { return sum + +!!x; }, stack.length);
@@ -33321,7 +33338,7 @@ var withTmpDir = function (handler) { return __awaiter(void 0, void 0, void 0, f
}
});
}); };
var createGif = function (grid0, chain, drawOptions, gifOptions) { return __awaiter(void 0, void 0, void 0, function () {
var createGif = function (grid0, cells, chain, drawOptions, animationOptions) { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, withTmpDir(function (dir) { return __awaiter(void 0, void 0, void 0, function () {
var _a, width, height, canvas, ctx, grid, stack, encoder, i, snake0, snake1, k, outFileName, optimizedFileName;
@@ -33333,17 +33350,17 @@ var createGif = function (grid0, chain, drawOptions, gifOptions) { return __awai
stack = [];
encoder = new gif_encoder_2_1["default"](width, height, "neuquant", true);
encoder.setRepeat(0);
encoder.setDelay(gifOptions.frameDuration);
encoder.setDelay(animationOptions.frameDuration);
encoder.start();
for (i = 0; i < chain.length; i += 1) {
snake0 = chain[i];
snake1 = chain[Math.min(chain.length - 1, i + 1)];
(0, step_1.step)(grid, stack, snake0);
for (k = 0; k < gifOptions.step; k++) {
for (k = 0; k < animationOptions.step; k++) {
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
(0, drawWorld_1.drawLerpWorld)(ctx, grid, snake0, snake1, stack, k / gifOptions.step, drawOptions);
(0, drawWorld_1.drawLerpWorld)(ctx, grid, cells, snake0, snake1, stack, k / animationOptions.step, drawOptions);
encoder.addFrame(ctx);
}
}
@@ -34468,9 +34485,8 @@ var getCellsFromGrid = function (_a) {
return Array.from({ length: height }, function (_, y) { return ({ x: x, y: y }); });
}).flat();
};
var createLivingCells = function (grid0, chain, drawOptions) {
var _a;
var cells = ((_a = drawOptions.cells) !== null && _a !== void 0 ? _a : getCellsFromGrid(grid0)).map(function (_a) {
var createLivingCells = function (grid0, chain, cells) {
var livingCells = (cells !== null && cells !== void 0 ? cells : getCellsFromGrid(grid0)).map(function (_a) {
var x = _a.x, y = _a.y;
return ({
x: x,
@@ -34486,23 +34502,23 @@ var createLivingCells = function (grid0, chain, drawOptions) {
var y = (0, snake_1.getHeadY)(snake);
if ((0, grid_1.isInside)(grid, x, y) && !(0, grid_1.isEmpty)((0, grid_1.getColor)(grid, x, y))) {
(0, grid_1.setColorEmpty)(grid, x, y);
var cell = cells.find(function (c) { return c.x === x && c.y === y; });
var cell = livingCells.find(function (c) { return c.x === x && c.y === y; });
cell.t = i / chain.length;
}
};
for (var i = 0; i < chain.length; i++) {
_loop_1(i);
}
return cells;
return livingCells;
};
var createSvg = function (grid, chain, drawOptions, gifOptions) {
var createSvg = function (grid, cells, chain, drawOptions, animationOptions) {
var width = (grid.width + 2) * drawOptions.sizeCell;
var height = (grid.height + 5) * drawOptions.sizeCell;
var duration = gifOptions.frameDuration * chain.length;
var cells = createLivingCells(grid, chain, drawOptions);
var duration = animationOptions.frameDuration * chain.length;
var livingCells = createLivingCells(grid, chain, cells);
var elements = [
(0, grid_2.createGrid)(cells, drawOptions, duration),
(0, stack_1.createStack)(cells, drawOptions, grid.width * drawOptions.sizeCell, (grid.height + 2) * drawOptions.sizeCell, duration),
(0, grid_2.createGrid)(livingCells, drawOptions, duration),
(0, stack_1.createStack)(livingCells, drawOptions, grid.width * drawOptions.sizeCell, (grid.height + 2) * drawOptions.sizeCell, duration),
(0, snake_2.createSnake)(chain, drawOptions, duration),
];
var viewBox = [