From 1036de2e9582c854d3f16fe6a3bd8f09059c24df Mon Sep 17 00:00:00 2001 From: Matt Swensen Date: Tue, 2 Aug 2022 13:59:40 +0000 Subject: [PATCH] WIP --- .vscode/launch.json | 21 +- cli/packages/wallpaper-curves/.gitignore | 2 + cli/packages/wallpaper-curves/.yarnrc | 2 + cli/packages/wallpaper-curves/README.md | 34 +++ cli/packages/wallpaper-curves/lib/index.js | 105 +++++++++ .../wallpaper-curves/lib/index.spec.js | 15 ++ cli/packages/wallpaper-curves/package.json | 37 +++ cli/packages/wallpaper-waves/.gitignore | 2 + cli/packages/wallpaper-waves/.yarnrc | 2 + cli/packages/wallpaper-waves/README.md | 34 +++ cli/packages/wallpaper-waves/lib/index.js | 218 ++++++++++++++++++ .../wallpaper-waves/lib/index.spec.js | 15 ++ cli/packages/wallpaper-waves/package.json | 37 +++ cli/yarn.lock | 7 +- 14 files changed, 528 insertions(+), 3 deletions(-) create mode 100644 cli/packages/wallpaper-curves/.gitignore create mode 100644 cli/packages/wallpaper-curves/.yarnrc create mode 100644 cli/packages/wallpaper-curves/README.md create mode 100644 cli/packages/wallpaper-curves/lib/index.js create mode 100644 cli/packages/wallpaper-curves/lib/index.spec.js create mode 100644 cli/packages/wallpaper-curves/package.json create mode 100644 cli/packages/wallpaper-waves/.gitignore create mode 100644 cli/packages/wallpaper-waves/.yarnrc create mode 100644 cli/packages/wallpaper-waves/README.md create mode 100644 cli/packages/wallpaper-waves/lib/index.js create mode 100644 cli/packages/wallpaper-waves/lib/index.spec.js create mode 100644 cli/packages/wallpaper-waves/package.json diff --git a/.vscode/launch.json b/.vscode/launch.json index c28895cc..2059e52f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "type": "pwa-node", + "type": "node", "request": "launch", "name": "Debug current test", "cwd": "${workspaceFolder}/cli", @@ -15,7 +15,7 @@ { "type": "node", "request": "launch", - "name": "Run", + "name": "Run and debug", "skipFiles": ["/**"], "cwd": "${workspaceFolder}/cli/packages", "program": "./themer/lib/index.js", @@ -27,6 +27,22 @@ "-o", "${workspaceFolder}/cli/tmp" ] + }, + { + "type": "node", + "request": "launch", + "name": "Quick run and debug current", + "skipFiles": ["/**"], + "cwd": "${workspaceFolder}/cli/packages", + "program": "./themer/lib/index.js", + "args": [ + "-c", + "./colors-default", + "-t", + "${file}", + "-o", + "${workspaceFolder}/cli/tmp" + ] } ], "inputs": [ @@ -93,6 +109,7 @@ "wallpaper-block-wave", "wallpaper-burst", "wallpaper-circuits", + "wallpaper-curves", "wallpaper-diamonds", "wallpaper-dot-grid", "wallpaper-octagon", diff --git a/cli/packages/wallpaper-curves/.gitignore b/cli/packages/wallpaper-curves/.gitignore new file mode 100644 index 00000000..4f1b7d8d --- /dev/null +++ b/cli/packages/wallpaper-curves/.gitignore @@ -0,0 +1,2 @@ +node_modules +LICENSE.md diff --git a/cli/packages/wallpaper-curves/.yarnrc b/cli/packages/wallpaper-curves/.yarnrc new file mode 100644 index 00000000..2740b156 --- /dev/null +++ b/cli/packages/wallpaper-curves/.yarnrc @@ -0,0 +1,2 @@ +version-tag-prefix "@themerdev/wallpaper-curves-v" +version-git-message "@themerdev/wallpaper-curves-v%s" diff --git a/cli/packages/wallpaper-curves/README.md b/cli/packages/wallpaper-curves/README.md new file mode 100644 index 00000000..011b4777 --- /dev/null +++ b/cli/packages/wallpaper-curves/README.md @@ -0,0 +1,34 @@ +# @themerdev/wallpaper-curves + +A wallpaper template for [themer](https://github.com/themerdev/themer). + +TODO: Add preview images + +## Installation & usage + +Install this module wherever you have `themer` installed: + + npm install @themerdev/wallpaper-curves + +Then pass `@themerdev/wallpaper-curves` as a `-t` (`--template`) arg to `themer`: + + themer -c my-colors.js -t @themerdev/wallpaper-curves -o gen + +`@themerdev/wallpaper-curves` will generate PNG wallpapers to the output directory (`gen/` in this example). + +### Default resolutions + +By default, `@themerdev/wallpaper-curves` will output wallpapers at the following sizes: + +- 2880 by 1800 (desktop) +- 750 by 1334 (device) + +### Custom resolutions + +`@themerdev/wallpaper-curves` adds the following argument to `themer`: + + --themer-wallpaper-curves-size + +to which you would pass `x`. For example, to forego the default resolutions and generate two wallpapers, one 1024 by 768 and one 320 by 960: + + themer -c my-colors.js -t @themerdev/wallpaper-curves --themer-wallpaper-curves-size 1024x768 --themer-wallpaper-curves-size 320x960 -o gen diff --git a/cli/packages/wallpaper-curves/lib/index.js b/cli/packages/wallpaper-curves/lib/index.js new file mode 100644 index 00000000..7fc336af --- /dev/null +++ b/cli/packages/wallpaper-curves/lib/index.js @@ -0,0 +1,105 @@ +const { + getSizesFromOptOrDefault, + deepFlatten, + colorSets: getColorSets, + listOutputFiles, +} = require('@themerdev/utils'); +const { createCanvas } = require('canvas'); +const { Bezier } = require('bezier-js'); + +const render = (colors, options) => { + try { + var sizes = getSizesFromOptOrDefault( + options['themer-wallpaper-curves-size'], + ); + } catch (e) { + return [Promise.reject(e.message)]; + } + + const colorSets = getColorSets(colors); + + return deepFlatten( + sizes.map((size) => + colorSets.map(async (colorSet) => { + const canvas = createCanvas(size.w, size.h); + const ctx = canvas.getContext('2d'); + + ctx.fillStyle = colorSet.colors.shade0; + ctx.fillRect(0, 0, size.w, size.h); + + const curve = new Bezier( + { x: size.w / 2 - 100, y: size.h / 2 - 100 }, + { x: size.w / 2 + 100, y: size.h / 2 + 100 }, + { x: size.w / 2 - 100, y: size.h / 2 + 100 }, + ); + + ctx.strokeStyle = `${colorSet.colors.shade7}03`; + ctx.lineCap = 'round'; + + for (let i = 128; i > 0; i--) { + ctx.lineWidth = i; + ctx.moveTo(curve.points[0].x, curve.points[0].y); + ctx.quadraticCurveTo( + curve.points[1].x, + curve.points[1].y, + curve.points[2].x, + curve.points[2].y, + ); + ctx.stroke(); + } + + // Fast but too small shadow blur + // ctx.shadowBlur = 50; + // ctx.shadowColor = colorSet.colors.shade7; + + ///////////////////// + // Slow noisy glow // + ///////////////////// + + // const { x: xMinMax, y: yMinMax } = curve.bbox(); + + // const pixelSize = 1; + + // for ( + // let x = xMinMax.min - 200; + // x <= xMinMax.max + 200; + // x += pixelSize + // ) { + // for ( + // let y = yMinMax.min - 200; + // y <= yMinMax.max + 200; + // y += pixelSize + // ) { + // const closest = curve.project({ x, y }); + // const distance = Math.sqrt( + // Math.pow(x - closest.x, 2) + Math.pow(y - closest.y, 2), + // ); + // const alpha = Math.max( + // 0, + // Math.min( + // 1, + // (1 - distance / Math.min(xMinMax.size, yMinMax.size)) * + // Math.random(), + // ), + // ); + // ctx.fillStyle = `rgba(255,255,0,${alpha})`; + // ctx.fillRect(x, y, pixelSize, pixelSize); + // } + // } + + return { + name: `themer-wallpaper-curves-${colorSet.name}-${size.w}x${size.h}.png`, + contents: Buffer.from( + canvas.toDataURL().replace('data:image/png;base64,', ''), + 'base64', + ), + }; + }), + ), + ); +}; + +module.exports = { + render, + renderInstructions: listOutputFiles, +}; diff --git a/cli/packages/wallpaper-curves/lib/index.spec.js b/cli/packages/wallpaper-curves/lib/index.spec.js new file mode 100644 index 00000000..7a47ec2c --- /dev/null +++ b/cli/packages/wallpaper-curves/lib/index.spec.js @@ -0,0 +1,15 @@ +const { render, renderInstructions } = require('./index'); +const { colors } = require('../../colors-default'); + +describe('themer "curves" wallpaper', () => { + it('should return 4 PNG files to write', async () => { + const files = await Promise.all(render(colors, {})); + expect(files.length).toBe(4); + expect(files.filter(file => /\.png/.test(file.name)).length).toBe(4); + }); + it('should list output files', async () => { + const files = await Promise.all(render(colors, { 'themer-wallpaper-curves-size': '1000x1000' })); + const instructions = renderInstructions(files.map(({ name }) => name)); + expect(instructions).toMatchSnapshot(); + }); +}); diff --git a/cli/packages/wallpaper-curves/package.json b/cli/packages/wallpaper-curves/package.json new file mode 100644 index 00000000..dff657b2 --- /dev/null +++ b/cli/packages/wallpaper-curves/package.json @@ -0,0 +1,37 @@ +{ + "name": "@themerdev/wallpaper-curves", + "version": "1.0.0", + "description": "A wallpaper generator for themer.", + "main": "lib/index.js", + "engines": { + "node": ">=8.11.4" + }, + "scripts": { + "prepublishOnly": "cp ../../../LICENSE.md ./" + }, + "author": "mjswensen", + "license": "MIT", + "files": [ + "/lib/index.js" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/themerdev/themer.git" + }, + "bugs": { + "url": "https://github.com/themerdev/themer/issues" + }, + "homepage": "https://github.com/themerdev/themer/tree/main/cli/packages/wallpaper-curves#readme", + "dependencies": { + "@themerdev/utils": "^1.0.0", + "bezier-js": "^6.1.0", + "canvas": "^2.6.1" + }, + "peerDependencies": { + "themer": "^3" + }, + "keywords": [ + "themer", + "wallpaper" + ] +} diff --git a/cli/packages/wallpaper-waves/.gitignore b/cli/packages/wallpaper-waves/.gitignore new file mode 100644 index 00000000..4f1b7d8d --- /dev/null +++ b/cli/packages/wallpaper-waves/.gitignore @@ -0,0 +1,2 @@ +node_modules +LICENSE.md diff --git a/cli/packages/wallpaper-waves/.yarnrc b/cli/packages/wallpaper-waves/.yarnrc new file mode 100644 index 00000000..3b8603c3 --- /dev/null +++ b/cli/packages/wallpaper-waves/.yarnrc @@ -0,0 +1,2 @@ +version-tag-prefix "@themerdev/wallpaper-waves-v" +version-git-message "@themerdev/wallpaper-waves-v%s" diff --git a/cli/packages/wallpaper-waves/README.md b/cli/packages/wallpaper-waves/README.md new file mode 100644 index 00000000..c1a98f80 --- /dev/null +++ b/cli/packages/wallpaper-waves/README.md @@ -0,0 +1,34 @@ +# @themerdev/wallpaper-waves + +A wallpaper template for [themer](https://github.com/themerdev/themer). + +TODO: Add preview images + +## Installation & usage + +Install this module wherever you have `themer` installed: + + npm install @themerdev/wallpaper-waves + +Then pass `@themerdev/wallpaper-waves` as a `-t` (`--template`) arg to `themer`: + + themer -c my-colors.js -t @themerdev/wallpaper-waves -o gen + +`@themerdev/wallpaper-waves` will generate PNG wallpapers to the output directory (`gen/` in this example). + +### Default resolutions + +By default, `@themerdev/wallpaper-waves` will output wallpapers at the following sizes: + +- 2880 by 1800 (desktop) +- 750 by 1334 (device) + +### Custom resolutions + +`@themerdev/wallpaper-waves` adds the following argument to `themer`: + + --themer-wallpaper-waves-size + +to which you would pass `x`. For example, to forego the default resolutions and generate two wallpapers, one 1024 by 768 and one 320 by 960: + + themer -c my-colors.js -t @themerdev/wallpaper-waves --themer-wallpaper-waves-size 1024x768 --themer-wallpaper-waves-size 320x960 -o gen diff --git a/cli/packages/wallpaper-waves/lib/index.js b/cli/packages/wallpaper-waves/lib/index.js new file mode 100644 index 00000000..db4fef3a --- /dev/null +++ b/cli/packages/wallpaper-waves/lib/index.js @@ -0,0 +1,218 @@ +const { + getSizesFromOptOrDefault, + deepFlatten, + colorSets: getColorSets, + listOutputFiles, +} = require('@themerdev/utils'); +const { createCanvas, loadImage } = require('canvas'); +const colorSteps = require('color-steps'); + +function randBetween(min, max) { + return Math.random() * (max - min) + min; +} + +// function randIntBetween(min, max) { +// return Math.floor(Math.random() * (max + 1 - min) + min); +// } + +// function pointsBetween(start, end, count) { +// return [ +// ...Array(count - 1) +// .fill(start) +// .map((start, i) => ({ +// x: start.x + ((end.x - start.x) / (count - 1)) * i, +// y: start.y + ((end.y - start.y) / (count - 1)) * i, +// })), +// end, +// ]; +// } + +function pointsBetween(start, end, totalCount) { + const step = (end - start) / (totalCount - 1); + return [ + start, + ...Array(totalCount - 2) + .fill(null) + .map((_, i) => step * (i + 1)), + end, + ]; +} + +const render = (colors, options) => { + try { + var sizes = getSizesFromOptOrDefault( + options['themer-wallpaper-waves-size'], + ); + } catch (e) { + return [Promise.reject(e.message)]; + } + + const colorSets = getColorSets(colors); + + return deepFlatten( + sizes.map((size) => + colorSets.map(async (colorSet) => { + const canvas = createCanvas(size.w, size.h); + const ctx = canvas.getContext('2d'); + + /* + const containerSize = Math.min(size.w, size.h) / 3; + const containerX = size.w / 2 - containerSize / 2, + containerY = size.h / 2 - containerSize / 2; + + const rowCount = 8; + const rowHeight = containerSize / rowCount; + const amplitude = rowHeight * 0.9; + + ctx.fillStyle = colorSet.colors.shade0; + ctx.fillRect(0, 0, size.w, size.h); + + colorSteps( + colorSet.colors.shade0, + colorSet.colors.accent0, + rowCount - 1, + ).forEach((color, rowIdx) => { + const rowY = containerY + rowIdx * rowHeight; + ctx.beginPath(); + ctx.moveTo(containerX, rowY); + const colCount = randIntBetween(3, 7); + const colSize = containerSize / colCount; + let lastControlPointY = null; + for (let col = 1; col <= colCount; col++) { + let controlPointY = randBetween(rowY - amplitude, rowY + amplitude); + if (lastControlPointY) { + if (lastControlPointY > rowY && controlPointY > rowY) { + controlPointY -= (controlPointY - rowY) * 2; + } else if (lastControlPointY < rowY && controlPointY < rowY) { + controlPointY += (rowY - controlPointY) * 2; + } + } + ctx.quadraticCurveTo( + containerX + colSize * (col - 0.5), + controlPointY, + containerX + colSize * col, + rowY, + ); + lastControlPointY = controlPointY; + } + ctx.lineTo(containerX + containerSize, containerY + containerSize); + ctx.lineTo(containerX, containerY + containerSize); + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); + }); + */ + + const containerSize = (Math.min(size.w, size.h) * 2) / 3; + const containerX = size.w / 2 - containerSize / 2, + containerY = size.h / 2 - containerSize / 2; + const rowCount = 8; + const rowHeight = containerSize / rowCount; + const shadeCount = rowCount - 1; + const amplitude = rowHeight * 1.5; + const leftControlYs = pointsBetween( + 0, + randBetween(-amplitude, amplitude), + shadeCount, + ); + const rightControlYs = pointsBetween( + 0, + randBetween(-amplitude, amplitude), + shadeCount, + ); + const leftControlX = containerX + containerSize / 3; + const rightControlX = containerX + (containerSize * 2) / 3; + + function waves(clipPathId, accentColor) { + return ` + + ${colorSteps(colorSet.colors.shade0, accentColor, shadeCount).map( + (color, i) => { + const rowY = containerY + rowHeight * i; + const rght = containerX + containerSize; + return ` + + `; + }, + )} + + `; + } + + const svg = ` + + + + + + + + + + + + + + ${waves('left', colorSet.colors.accent0)} + ${waves('center', colorSet.colors.accent2)} + ${waves('right', colorSet.colors.accent7)} + + `; + + const url = `data:image/svg+xml;base64,${Buffer.from( + svg, + 'utf8', + ).toString('base64')}`; + const img = await loadImage(url); + ctx.drawImage(img, 0, 0); + + return { + name: `themer-wallpaper-waves-${colorSet.name}-${size.w}x${size.h}.png`, + contents: Buffer.from( + canvas.toDataURL().replace('data:image/png;base64,', ''), + 'base64', + ), + }; + }), + ), + ); +}; + +module.exports = { + render, + renderInstructions: listOutputFiles, +}; diff --git a/cli/packages/wallpaper-waves/lib/index.spec.js b/cli/packages/wallpaper-waves/lib/index.spec.js new file mode 100644 index 00000000..0113e32c --- /dev/null +++ b/cli/packages/wallpaper-waves/lib/index.spec.js @@ -0,0 +1,15 @@ +const { render, renderInstructions } = require('./index'); +const { colors } = require('../../colors-default'); + +describe('themer "waves" wallpaper', () => { + it('should return 4 PNG files to write', async () => { + const files = await Promise.all(render(colors, {})); + expect(files.length).toBe(4); + expect(files.filter(file => /\.png/.test(file.name)).length).toBe(4); + }); + it('should list output files', async () => { + const files = await Promise.all(render(colors, { 'themer-wallpaper-waves-size': '1000x1000' })); + const instructions = renderInstructions(files.map(({ name }) => name)); + expect(instructions).toMatchSnapshot(); + }); +}); diff --git a/cli/packages/wallpaper-waves/package.json b/cli/packages/wallpaper-waves/package.json new file mode 100644 index 00000000..678f00fe --- /dev/null +++ b/cli/packages/wallpaper-waves/package.json @@ -0,0 +1,37 @@ +{ + "name": "@themerdev/wallpaper-waves", + "version": "1.0.0", + "description": "A wallpaper generator for themer.", + "main": "lib/index.js", + "engines": { + "node": ">=8.11.4" + }, + "scripts": { + "prepublishOnly": "cp ../../../LICENSE.md ./" + }, + "author": "mjswensen", + "license": "MIT", + "files": [ + "/lib/index.js" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/themerdev/themer.git" + }, + "bugs": { + "url": "https://github.com/themerdev/themer/issues" + }, + "homepage": "https://github.com/themerdev/themer/tree/main/cli/packages/wallpaper-waves#readme", + "dependencies": { + "@themerdev/utils": "^1.0.0", + "canvas": "^2.6.1", + "color-steps": "^1.0.2" + }, + "peerDependencies": { + "themer": "^3" + }, + "keywords": [ + "themer", + "wallpaper" + ] +} diff --git a/cli/yarn.lock b/cli/yarn.lock index b0cbbbff..f4707505 100644 --- a/cli/yarn.lock +++ b/cli/yarn.lock @@ -812,6 +812,11 @@ base64-js@^1.5.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bezier-js@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/bezier-js/-/bezier-js-6.1.0.tgz#162b7bdbabe866e3a796285a89d71085140755ec" + integrity sha512-oc8fkHqG0R+dQuNiXVbPMB0cc8iDqkLAjbA2gq26QmV8tZqW9GGI7iNEX1ioRWlZperQS7v5BX03+9FLVWZbSw== + bl@^2.0.1: version "2.2.1" resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5" @@ -989,7 +994,7 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-steps@^1.0.1: +color-steps@^1.0.1, color-steps@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/color-steps/-/color-steps-1.0.2.tgz#798767b8055aca5d1b8183ab31da07b2403bddbb" integrity sha512-Pj8QfvoFakPb6kPcovfbJzoxPObzYUj7XhNCO2xiC7z8TdjOoMNwMOyIa2nLFP6JKssnAHAfI8EaqhaJg3uWVg==