mirror of
https://github.com/seigler/generative
synced 2025-07-26 06:46:10 +00:00
Sketch 8, Terra Firma
This commit is contained in:
parent
21769e9ffb
commit
57ac17c916
10 changed files with 239 additions and 8 deletions
210
app/8/index.js
Normal file
210
app/8/index.js
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
new p5(sketch => {
|
||||||
|
sketch.disableFriendlyErrors = true;
|
||||||
|
// reused dimensions and a seed
|
||||||
|
let seed, width, height, noiseResolution, overdraw, blurQuality;
|
||||||
|
const layers = {}; // offscreen layers
|
||||||
|
const shaders = {}; // shaders
|
||||||
|
const lib = {}; // libraries
|
||||||
|
const assets = {}; // fonts, images, sound files
|
||||||
|
|
||||||
|
sketch.preload = () => {
|
||||||
|
shaders.whiteNoise = sketch.loadShader(
|
||||||
|
"../shaders/base.vert",
|
||||||
|
"../shaders/white-noise.frag"
|
||||||
|
);
|
||||||
|
assets.stroke = sketch.loadImage("./brush-100x30.png");
|
||||||
|
};
|
||||||
|
|
||||||
|
sketch.setup = () => {
|
||||||
|
filenamePrefix = "seigler-p5-8-terra-firma-";
|
||||||
|
overdraw = 0.1;
|
||||||
|
width = Math.floor(sketch.windowWidth * (1 + overdraw));
|
||||||
|
height = Math.floor(sketch.windowHeight * (1 + overdraw));
|
||||||
|
noiseResolution = [0.2, 0.1, 0.05, 2];
|
||||||
|
blurQuality = 2;
|
||||||
|
|
||||||
|
window.onhashchange = () => {
|
||||||
|
seed = window.location.hash.substr(1);
|
||||||
|
generate();
|
||||||
|
};
|
||||||
|
|
||||||
|
seed = window.location.hash.substr(1);
|
||||||
|
sketch.colorMode(sketch.HSL, 1);
|
||||||
|
|
||||||
|
sketch.createCanvas(sketch.windowWidth, sketch.windowHeight);
|
||||||
|
|
||||||
|
layers.contours = sketch.createGraphics(width, height);
|
||||||
|
layers.contours.colorMode(sketch.HSL, 1);
|
||||||
|
|
||||||
|
layers.noise = sketch.createGraphics(width, height, sketch.WEBGL);
|
||||||
|
|
||||||
|
// layers.blur1 = sketch.createGraphics(width, height, sketch.WEBGL);
|
||||||
|
// layers.blur2 = sketch.createGraphics(width, height, sketch.WEBGL);
|
||||||
|
|
||||||
|
generate();
|
||||||
|
};
|
||||||
|
|
||||||
|
sketch.draw = () => {};
|
||||||
|
|
||||||
|
sketch.keyReleased = () => {
|
||||||
|
if (sketch.key == " ") {
|
||||||
|
seed = null;
|
||||||
|
generate();
|
||||||
|
} else if (sketch.key == "s") {
|
||||||
|
sketch.saveCanvas(filenamePrefix + seed + ".jpg", "jpg");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sketch.doubleClicked = () => {
|
||||||
|
seed = null;
|
||||||
|
generate();
|
||||||
|
};
|
||||||
|
|
||||||
|
let resizeTimer;
|
||||||
|
sketch.windowResized = () => {
|
||||||
|
clearTimeout(resizeTimer);
|
||||||
|
resizeTimer = setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
function generate() {
|
||||||
|
if (seed) {
|
||||||
|
sketch.randomSeed(seed);
|
||||||
|
} else {
|
||||||
|
const seed = Math.floor(sketch.random(1000000000000));
|
||||||
|
window.location.hash = seed;
|
||||||
|
sketch.randomSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
sketch.noiseSeed(sketch.random(0, 1000000000));
|
||||||
|
lib.simplex = new SimplexNoise(sketch.random(0, 1000000000));
|
||||||
|
|
||||||
|
layers.contours.clear();
|
||||||
|
drawContours(layers.contours);
|
||||||
|
|
||||||
|
sketch.blendMode(sketch.BLEND);
|
||||||
|
sketch.background(0);
|
||||||
|
layers.noise.shader(shaders.whiteNoise);
|
||||||
|
shaders.whiteNoise.setUniform("u_resolution", [width, height]);
|
||||||
|
shaders.whiteNoise.setUniform("u_alpha", 0.05);
|
||||||
|
layers.noise.rect(0, 0, width, height);
|
||||||
|
sketch.image(
|
||||||
|
layers.contours,
|
||||||
|
Math.round((-width * overdraw) / 2),
|
||||||
|
Math.round((-height * overdraw) / 2)
|
||||||
|
);
|
||||||
|
sketch.blendMode(sketch.OVERLAY);
|
||||||
|
sketch.image(layers.noise, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawContours(layer) {
|
||||||
|
const noiseScale = 3 / (width + height);
|
||||||
|
const quant = Math.round(Math.sqrt(width * height) / 1440 * 100); // 100 at 1920*1080
|
||||||
|
layers.contours.imageMode(sketch.CENTER);
|
||||||
|
|
||||||
|
const dots = buildGrid(0, 0, width, height, 15 * quant);
|
||||||
|
layer.noStroke();
|
||||||
|
dots.forEach(({x, y}) => {
|
||||||
|
const {noise, angle} = noisePlus(x, y, noiseScale);
|
||||||
|
if (noise < 0.5) { // water
|
||||||
|
layer.tint(218/360, 0.8, 0.1 + sketch.random(0.05) + noise * 2 * 0.2);
|
||||||
|
} else { // land
|
||||||
|
layer.tint(119/360, 0.8, 0.1 + sketch.random(0.05) + (noise - 0.5) * 2 * 0.2);
|
||||||
|
}
|
||||||
|
const size = 200;
|
||||||
|
layer.translate(x, y);
|
||||||
|
layer.rotate(angle + (noise < 0.5 ? sketch.HALF_PI : 0));
|
||||||
|
layer.image(assets.stroke, 0, 0, size, size / 3);
|
||||||
|
layer.resetMatrix();
|
||||||
|
});
|
||||||
|
|
||||||
|
const trails = buildGrid(0, 0, width, height, 5 * quant);
|
||||||
|
layer.noTint();
|
||||||
|
layer.strokeCap(sketch.SQUARE);
|
||||||
|
trails.forEach((point) => {
|
||||||
|
const x = point.x + sketch.random(-50, 50);
|
||||||
|
const y = point.y + sketch.random(-50, 50);
|
||||||
|
const noise = fractalNoise(x, y, noiseScale);
|
||||||
|
for (let i = 0, max = quant/2, aX = x, aY = y; i < max; i++) {
|
||||||
|
const {angle} = noisePlus(aX, aY, noiseScale);
|
||||||
|
const dX = Math.cos(angle + sketch.HALF_PI) * 4;
|
||||||
|
const dY = Math.sin(angle + sketch.HALF_PI) * 4;
|
||||||
|
if (noise > 0.5) {
|
||||||
|
layer.stroke(0, 0.8);
|
||||||
|
} else {
|
||||||
|
layer.stroke(1, 0.5);
|
||||||
|
}
|
||||||
|
layer.strokeWeight(Math.sin(i/max*Math.PI)); // smooth 0 to 1 to 0
|
||||||
|
layer.line(aX, aY, aX + dX, aY + dY);
|
||||||
|
aX += dX;
|
||||||
|
aY += dY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildGrid(minX, minY, maxX, maxY, approxPoints) {
|
||||||
|
const width = maxX - minX;
|
||||||
|
const height = maxY - minY;
|
||||||
|
const unit = Math.sqrt(width * height / Math.sin(Math.PI / 3) / approxPoints);
|
||||||
|
const rows =
|
||||||
|
Math.max(1, Math.round(height / unit / Math.sin(Math.PI / 3))) + 1;
|
||||||
|
const cols = Math.max(1, Math.round(width / unit)) + 1;
|
||||||
|
const grid = [];
|
||||||
|
for (let index = 0; index < rows * cols; index++) {
|
||||||
|
const col = index % cols;
|
||||||
|
const row = Math.floor(index / cols);
|
||||||
|
grid.push({
|
||||||
|
x: minX + (width / (cols - 1)) * col + (((row % 2) - 0.5) * unit) / 2,
|
||||||
|
y: minY + (height / (rows - 1)) * row
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function noisePlus(x, y, noiseScale = 2 / (width + height), noiseOffset = 0) {
|
||||||
|
const noise = fractalNoise(x, y, noiseScale, noiseOffset);
|
||||||
|
const dX = fractalNoise(x + 1, y, noiseScale, noiseOffset) - noise;
|
||||||
|
const dY = fractalNoise(x, y + 1, noiseScale, noiseOffset) - noise;
|
||||||
|
const angle = Math.atan2(dY, dX);
|
||||||
|
const length = sketch.dist(0, 0, dX, dY);
|
||||||
|
return ({noise, angle, length});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fractalNoise(x, y, noiseScale = 2 / (width + height), noiseOffset = 0) {
|
||||||
|
return 0.5 + 0.5 * (
|
||||||
|
lib.simplex.noise2D(
|
||||||
|
noiseOffset + noiseScale * x,
|
||||||
|
noiseOffset + noiseScale * y
|
||||||
|
) +
|
||||||
|
0.5 * lib.simplex.noise2D(
|
||||||
|
noiseOffset + 2 * noiseScale * x,
|
||||||
|
noiseOffset + 2 * noiseScale * y
|
||||||
|
) +
|
||||||
|
0.25 * lib.simplex.noise2D(
|
||||||
|
noiseOffset + 4 * noiseScale * x,
|
||||||
|
noiseOffset + 4 * noiseScale * y
|
||||||
|
)
|
||||||
|
) / 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
// give normally distributed values from 0-1 a uniform distribution
|
||||||
|
function flattenDistribution(x) {
|
||||||
|
return (
|
||||||
|
23.8615 * Math.pow(x, 5) -
|
||||||
|
59.6041 * Math.pow(x, 4) +
|
||||||
|
47.2472 * Math.pow(x, 3) -
|
||||||
|
11.3053 * Math.pow(x, 2) +
|
||||||
|
0.806219 * x -
|
||||||
|
0.00259101
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns abs angle from a to b to c
|
||||||
|
function three_point_angle(A, B, C) {
|
||||||
|
const AB = Math.sqrt(Math.pow(B.x - A.x, 2) + Math.pow(B.y - A.y, 2));
|
||||||
|
const BC = Math.sqrt(Math.pow(B.x - C.x, 2) + Math.pow(B.y - C.y, 2));
|
||||||
|
const AC = Math.sqrt(Math.pow(C.x - A.x, 2) + Math.pow(C.y - A.y, 2));
|
||||||
|
return Math.acos((BC * BC + AB * AB - AC * AC) / (2 * BC * AB));
|
||||||
|
}
|
||||||
|
});
|
7
app/8/index.static.hbs
Normal file
7
app/8/index.static.hbs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: Terra Firma
|
||||||
|
created: 2019-11-18
|
||||||
|
index: 8
|
||||||
|
_options:
|
||||||
|
layout: app/layouts/sketch.hbs
|
||||||
|
---
|
BIN
app/assets/8/brush-100x30.png
Normal file
BIN
app/assets/8/brush-100x30.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
app/assets/8/brush-911x185.png
Normal file
BIN
app/assets/8/brush-911x185.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
app/assets/8/example.jpg
Normal file
BIN
app/assets/8/example.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 466 KiB |
|
@ -14,6 +14,12 @@ _options:
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<h1>P5.js generative art</h1>
|
<h1>P5.js generative art</h1>
|
||||||
|
{{> preview
|
||||||
|
index="8"
|
||||||
|
date="2019-11-18"
|
||||||
|
title="Terra Firma"
|
||||||
|
alt="a brush stroked map of sea and land, with breezy black and white contour lines marking the elevation"
|
||||||
|
}}
|
||||||
{{> preview
|
{{> preview
|
||||||
index="7"
|
index="7"
|
||||||
date="2019-11-16"
|
date="2019-11-16"
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<body>
|
<body>
|
||||||
<script src="../modules/p5.min.js"></script>
|
<script src="../modules/p5.min.js"></script>
|
||||||
<script src="../modules/rhill-voronoi-core.min.js"></script>
|
<script src="../modules/rhill-voronoi-core.min.js"></script>
|
||||||
|
<script src="../modules/simplex-noise.js"></script>
|
||||||
<script src="DIGEST(../app.js)"></script>
|
<script src="DIGEST(../app.js)"></script>
|
||||||
<footer><a href="../">Back</a> | <code>Space</code> or double click for a new one. <code>S</code> to save.</footer>
|
<footer><a href="../">Back</a> | <code>Space</code> or double click for a new one. <code>S</code> to save.</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -21,7 +21,8 @@ exports.plugins = {
|
||||||
modules: [
|
modules: [
|
||||||
'node_modules/p5/lib/p5.min.js',
|
'node_modules/p5/lib/p5.min.js',
|
||||||
'node_modules/p5/lib/addons/p5.sound.min.js',
|
'node_modules/p5/lib/addons/p5.sound.min.js',
|
||||||
'node_modules/voronoi/rhill-voronoi-core.min.js'
|
'node_modules/voronoi/rhill-voronoi-core.min.js',
|
||||||
|
'node_modules/simplex-noise/simplex-noise.js'
|
||||||
],
|
],
|
||||||
verbose : true,
|
verbose : true,
|
||||||
onlyChanged: true
|
onlyChanged: true
|
||||||
|
|
17
package-lock.json
generated
17
package-lock.json
generated
|
@ -3338,9 +3338,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.5.1",
|
"version": "4.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.2.tgz",
|
||||||
"integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==",
|
"integrity": "sha512-29Zxv/cynYB7mkT1rVWQnV7mGX6v7H/miQ6dbEpYTKq5eJBN7PsRB+ViYJlcT6JINTSu4dVB9kOqEun78h6Exg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
|
@ -3363,9 +3363,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
"version": "3.6.8",
|
"version": "3.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.8.tgz",
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz",
|
||||||
"integrity": "sha512-XhHJ3S3ZyMwP8kY1Gkugqx3CJh2C3O0y8NPiSxtm1tyD/pktLAkFZsFGpuNfTZddKDQ/bbDBLAd2YyA1pbi8HQ==",
|
"integrity": "sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -5500,6 +5500,11 @@
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"simplex-noise": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-OjyDWm/QZjVbMrPxDVi9b2as+SeNn9EBXlrWVRlFW+TSyWMSXouDryXkQN0vf5YP+QZKobrmkvx1eQYPLtuqfw=="
|
||||||
|
},
|
||||||
"since-app-start": {
|
"since-app-start": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/since-app-start/-/since-app-start-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/since-app-start/-/since-app-start-0.3.3.tgz",
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"p5": "^0.10.2",
|
"p5": "^0.10.2",
|
||||||
|
"simplex-noise": "^2.4.0",
|
||||||
"voronoi": "^1.0.0"
|
"voronoi": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -23,7 +24,7 @@
|
||||||
"copycat-brunch": "^1.1.1",
|
"copycat-brunch": "^1.1.1",
|
||||||
"digest-brunch": "^1.6.0",
|
"digest-brunch": "^1.6.0",
|
||||||
"git-directory-deploy": "^1.5.1",
|
"git-directory-deploy": "^1.5.1",
|
||||||
"handlebars": "^4.5.1",
|
"handlebars": "^4.5.2",
|
||||||
"html-brunch-static": "^1.4.1",
|
"html-brunch-static": "^1.4.1",
|
||||||
"uglify-js-brunch": "^2.10.0"
|
"uglify-js-brunch": "^2.10.0"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue