Initial commit! 🎉
This commit is contained in:
		
						commit
						bb1b543721
					
				| 
						 | 
					@ -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/
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -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();
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					export * from './base.js';
 | 
				
			||||||
 | 
					export * from './crop.js';
 | 
				
			||||||
 | 
					export * from './generic.js';
 | 
				
			||||||
 | 
					export * from './simplex-noise.js';
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "compilerOptions": {
 | 
				
			||||||
 | 
					    "esModuleInterop": true,
 | 
				
			||||||
 | 
					    "module": "ESNext",
 | 
				
			||||||
 | 
					    "moduleResolution": "Node",
 | 
				
			||||||
 | 
					    "outDir": "build",
 | 
				
			||||||
 | 
					    "strict": true,
 | 
				
			||||||
 | 
					    "target": "ESNext"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "include": [
 | 
				
			||||||
 | 
					    "source/**/*.ts"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue