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"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue