Initial commit! 🎉

This commit is contained in:
Bauke 2022-03-07 16:33:44 +01:00
commit bb1b543721
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
11 changed files with 2980 additions and 0 deletions

107
.gitignore vendored Normal file
View File

@ -0,0 +1,107 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Image & data output directory
output/

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "driftingnebula",
"description": "Generative art with GIMP, GEGL and ImageMagick.",
"version": "1.0.0",
"scripts": {
"start": "node --loader=ts-node/esm --no-warnings source/driftingnebula.ts",
"test": "xo"
},
"dependencies": {
"execa": "^6.1.0"
},
"devDependencies": {
"@types/node": "^17.0.21",
"ts-node": "^10.6.0",
"typescript": "^4.6.2",
"xo": "^0.48.0"
},
"type": "module",
"xo": {
"prettier": true,
"rules": {
"no-await-in-loop": "off"
},
"space": true
}
}

2671
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

53
source/driftingnebula.ts Normal file
View File

@ -0,0 +1,53 @@
import fsp from 'node:fs/promises';
import path from 'node:path';
import {performance} from 'node:perf_hooks';
import {execa} from 'execa';
import {Crop} from './gegl/exports.js';
async function main(): Promise<void> {
const projects: Project[] = [];
for (const {name, operations, resolution} of projects) {
const dataStart = performance.now();
const {width, height} = resolution;
const baseDir = path.resolve(`./output/${name}`);
await fsp.mkdir(baseDir, {recursive: true});
console.log(`# ${name}`);
console.log(`* ${width}x${height}`);
console.log(`* ${operations.length} operations`);
const graph = operations.flatMap((operation) => {
const graph = operation.graph();
if (operation.appendCrop) {
graph.push(...new Crop({height, width}).graph());
}
return graph;
});
const prettyGraph = graph.map((operation) =>
operation.startsWith('gegl:') ? `\n${operation}\n` : ` ${operation}\n`,
);
const graphFile = `${name}.txt`;
const outputFile = `${name}.png`;
console.log(`* Writing ${graphFile}`);
await fsp.writeFile(
path.join(baseDir, graphFile),
prettyGraph.join('').trimStart(),
);
console.log(`* Writing ${outputFile}`);
await execa('gegl', ['-o', path.join(baseDir, outputFile), '--', ...graph]);
const time = (performance.now() - dataStart).toFixed(2);
console.log(`* Generated in ${time}ms`);
}
}
void main();

28
source/gegl/base.ts Normal file
View File

@ -0,0 +1,28 @@
export abstract class BaseOperation<P> {
public parameters: P;
/**
* Some GEGL operations will run infinitely unless you limit the buffer in
* some way, so all operations must indicate whether or not they should be
* followed by a crop operation.
*/
public abstract appendCrop: boolean;
/** The GEGL operation name, starting with `gegl:`. */
public abstract name: string;
constructor(parameters: P) {
this.parameters = parameters;
}
public graph(): string[] {
const graph: string[] = [this.name];
for (const [key, value] of Object.entries(this.parameters)) {
const kebabCasedKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
graph.push(`${kebabCasedKey}=${value as string}`);
}
return graph;
}
}

26
source/gegl/crop.ts Normal file
View File

@ -0,0 +1,26 @@
import {BaseOperation} from './base.js';
export interface CropParameters {
height: number;
resetOrigin: boolean;
width: number;
x: number;
y: number;
}
export class Crop extends BaseOperation<CropParameters> {
public static default: CropParameters = {
height: 0,
resetOrigin: false,
width: 0,
x: 0,
y: 0,
};
public appendCrop = false;
public name = 'gegl:crop';
constructor(parameters?: Partial<CropParameters>) {
super({...Crop.default, ...parameters});
}
}

4
source/gegl/exports.ts Normal file
View File

@ -0,0 +1,4 @@
export * from './base.js';
export * from './crop.js';
export * from './generic.js';
export * from './simplex-noise.js';

18
source/gegl/generic.ts Normal file
View File

@ -0,0 +1,18 @@
import {BaseOperation} from './base.js';
export type GenericParameters = Record<string, number | string>;
export class Generic extends BaseOperation<GenericParameters> {
public appendCrop: boolean;
public name: string;
constructor(
name: string,
parameters?: GenericParameters,
appendCrop?: boolean,
) {
super(parameters ?? {});
this.name = name;
this.appendCrop = appendCrop ?? false;
}
}

View File

@ -0,0 +1,22 @@
import {BaseOperation} from './base.js';
export interface SimplexNoiseParameters {
iterations: number;
scale: number;
seed: number;
}
export class SimplexNoise extends BaseOperation<SimplexNoiseParameters> {
public static default: SimplexNoiseParameters = {
iterations: 1,
scale: 1,
seed: 1,
};
public appendCrop = true;
public name = 'gegl:simplex-noise';
constructor(parameters?: Partial<SimplexNoiseParameters>) {
super({...SimplexNoise.default, ...parameters});
}
}

12
source/types.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
import {BaseOperation} from './gegl/exports.js';
declare global {
interface Project {
name: string;
operations: Array<InstanceType<typeof BaseOperation>>;
resolution: {
height: number;
width: number;
};
}
}

13
tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"esModuleInterop": true,
"module": "ESNext",
"moduleResolution": "Node",
"outDir": "build",
"strict": true,
"target": "ESNext"
},
"include": [
"source/**/*.ts"
]
}