xxxxxxxxxx
507
//
// there is a render difference between using Processing P5 and WEB P5
// for Web P5 (and the Editor) use
// .strokeWeight(2);// for terrain etc.
//
// using an array for checking stroke values vs using get() which is extremely slow
let unit = 1; // unit size in pixels. Also, one block equals one unit.
let noiseXY = 0.005; // higher value = more granularity.also pseudo scale. Default value:0.005
let vertices = [];
let refX, refY; // reference coordinates for sliding noise cutoff.
let distM; // max dist between ref and any coordinate. Serves as a max noise reference
let distR;
let noiseR;
let noiseLod;
let highestV = 0.3;
let lowestV = 0.6;
let lowestX, lowestY;
let highestX, highestY;
let shift = 0;
let noiseP = 0;
let rowtest = []; // array used for storing pixel color, to compare for iso lines
// colors must be sorted lowest to heighest
let color = [
"#aee9ff",
"#dfedc8",
"#cfe4b4",
"#fff7df",
"#fcebbb",
"#fbd68e",
"#e9bf9c",
"#fcb47e",
"#bc8f40",
"#FFFFFF",
];
let mtName = [
"Accomplish",
"Amazing",
"Bliss",
"Brave",
"Calm",
"Dazzling",
"Delight",
"Divine",
"Earnest",
"Fabulous",
"Fair",
"Glamorous",
"Hug",
"Harmonious",
"Imagine",
"Keen",
"Light",
"Luminous",
"Marvelous",
"Now",
"Novel",
"Okay",
"Pleasant",
"Progress",
"Rejoice",
"Robust",
"Sunny",
"Tranquil",
"Upright",
"Victory",
"Virtuous",
"Whole",
"Welcome",
"Zeal",
];
// particle vars
let quiteaFew = 2;
let xorigin, yorigin;
let particle = [];
let dir;
let speed = 1.0;
// grid
let gridOverlay;
// user control
let sliderNoise;
let sliderElevation;
let sliderSmooth;
let button;
let bird;
function preload() {
bird = loadImage('Albatross.svg');
}
function setup() {
createCanvas(800, 800);
mapImage = createGraphics(800, 800);
gridOverlay = createGraphics(760, 760);
iconOverlay = createGraphics(800, 800);
animatedOverlay = createGraphics(760, 760);
background(190);
cursor(CROSS);
strokeCap(SQUARE);
//imageMode(CENTER);
bird.resize(128,128);
// bird/particle array random space assignment
for (let i = 0; i < quiteaFew ; i++) {
let rx = int(random(0, width));
let ry = int(random(0, height));
particle.push(createVector(rx, ry));
}
// user control, only once
sliderNoise = createSlider(0.0001, 0.01, 0.008, 0.001); // level of detail
sliderNoise.position(0, height);
slidernoiseLod = createSlider(1, 9, 5, 1); // union vs fragmentation
slidernoiseLod.position(200, height);
sliderSmooth = createSlider(0, 255, 127); // unassigned
sliderSmooth.position(400, height);
button = createButton("renew");
button.position(600, height);
button.mousePressed(mapvarInit);
mapvarInit();
// wind pattern variables
noiseDetail(5, 0.5);
noiseXY = 0.0005;
speed = 0.3;
strokeWeight(8);
stroke(255, 100);
}
function draw() {
// chart render
image(mapImage, 0, 0);
image(iconOverlay, 0, 0);
image(gridOverlay, 20, 20);
wind();// un/comment birds
image(animatedOverlay,20,20);
fill(255, 90, 0, 10);
square(0, 0, width); // map tint
}
function mapvarInit() {
background(190);
clear();
mapImage.clear();
gridOverlay.clear();
iconOverlay.clear();
// noiseSeed(1900);
highestV = 0.3;
lowestV = 0.6;
shift = 0;
// Map noise center location, not near edges
refX = int(random(0.3 * width, 0.7 * width));
refY = int(random(0.3 * height, 0.7 * height));
if (refX < width / 2) {
maxDx = width - refX;
} else maxDx = refX;
if (refY < width / 2) {
maxDy = width - refY;
} else maxDy = refY;
distM = sqrt(sq(maxDx) + sq(maxDy));
// variable output
print("ref x,y: " + refX + "," + refY); // center but not necessarily highest elevation
print("distM: " + distM);
print("lowestV:" + lowestV + " highestV: " + highestV);
print("particle.length: " + particle.length);
// user input
let val1 = sliderNoise.value();
let val2 = slidernoiseLod.value();
noiseXY = val1;
noiseLod = val2;
// chart generation
elevationGradients(); // creates heightmap
latlon();
mapIcons(refX, refY, lowestX, lowestY, highestX, highestY);
// chart render
image(mapImage, 0, 0);
image(iconOverlay, 0, 0);
image(gridOverlay, 20, 20);
fill(255, 90, 0, 10);
square(0, 0, width); // map tint
}
function wind() {
for (let i = 0; i < particle.length; i++) {
//float noiseP = noise(particle[i].x*noiseXY, particle[i].y*noiseXY);//
let noiseP = noise(particle[i].x * noiseXY, particle[i].y * noiseXY); //
let angle = TWO_PI * noiseP;
//println(noiseP);
let x = cos(angle);
let y = sin(angle);
// for particles 20px within border, for birds 20px outside of border
if (
particle[i].x > width + 20 ||
particle[i].x < -20 ||
particle[i].y < -20 ||
particle[i].y > height + 20
) {
particle[i] = createVector(
int(random(20, width - 20)),
int(random(20, height - 20))
);
}
dir = createVector(x, y);
particle[i].add(dir.mult(speed+(i*0.2)));// variable speed
/// Windfeathers
animatedOverlay.noFill();
animatedOverlay.strokeWeight(0.8);
animatedOverlay.stroke(255);
animatedOverlay.point(particle[i].x, particle[i].y);
//animatedOverlay.circle(100,100,100);// debug
/*
/// Birds
push();
imageMode(CENTER);
translate(particle[i].x, particle[i].y);
rotate(angle+0.5*PI);
image(bird,0,0);
pop();
*/
}
}
function elevationGradients() {
for (let row = 0; row < height; row += unit) {
shift += 1;
for (let col = 0; col < width; col += unit) {
let distR = dist(refX, refY, col, row);
let noiseR = 0.8 - (distR / distM) * 0.6;
// noiseDetail(noiseLod, noiseR);// sometimes exceeds 1.0
noiseDetail(noiseLod, noiseR); // may exceed 1.0
noiseP = noise(col * noiseXY, row * noiseXY); //
if (noiseP > highestV) {
highestV = noiseP;
highestX = col;
highestY = row;
}
if (noiseP < lowestV) {
lowestV = noiseP;
lowestX = col;
lowestY = row;
}
let strokeV = int(map(round(noiseP * 10), 0.0, 10.0, 50, 255));
// greyscale background
if (noiseP > 0.5) {
mapImage.stroke(strokeV); // use local server
mapImage.strokeWeight(2); // use for web
mapImage.point(col, row);
} else {
strokeV = 200; // OCEAN color, needed to compare stroke colors for 'coastline'
// write pixels to avoid empties
mapImage.stroke(strokeV); // use local server
mapImage.strokeWeight(2); // use for web
mapImage.point(col, row);
}
/*
// color background
if (noiseP > 0.5) {
clr = int(noiseP * 10);
if (clr > 9) {
clr = 9;
}
mapImage.stroke(2);
mapImage.stroke(color[clr]); //color
} else stroke(color[0]);// blue
mapImage.point(col, row);
*/
// Isolines
// check for difference in color, pixel above and before
if (
strokeV != rowtest[rowtest.length - 1] ||
strokeV != rowtest[rowtest.length - 800]
) {
// render a broader coastline
if (strokeV == 200) {
mapImage.stroke(80); // slightly darker coastline
mapImage.strokeWeight(4);
} else {
mapImage.strokeWeight(2);
mapImage.stroke(90);
}
mapImage.point(col, row);
}
rowtest.push(strokeV);
// Hatching \\
// Land
let moduloP = int(noiseP * 10.0); // Modulo requires a 'forced' INT !!!
let checkX = (col + shift) % moduloP;
let checkY = int(row) % 6;
if (noiseP > 0.5 && checkX == 0) {
// variable strokeWeight according to noise level
// mapImage.strokeWeight (0.3+(1.0-map(noiseP, 0.0, 1.0, 0.3, 0.9)));
// mapImage.stroke(map(noiseP, 0.0, 1.0, 1, 100));// keep stroke in range
mapImage.strokeWeight(2);
mapImage.stroke(0);
// only draw by grace of modulo, to create white space and depend on noiseP
let randomGlitch = round(random(0, noiseP * 20));
if (randomGlitch < 10) {
mapImage.point(col, row);
}
}
// Ocean Horizontal Lines
if (checkY == 0 && noiseP < 0.5) {
mapImage.strokeWeight(1);
mapImage.stroke(0);
let randomGlitch = int(random(0, 20));
if (randomGlitch < 8) {
mapImage.point(col, row);
}
}
}
}
}
function latlon() {
let latitudeD = int(random(0, 70)); // in degrees
let longitudeD = int(random(-180, 180)); //
let degtoRad = (TWO_PI / 360.0) * latitudeD; // deg to radians
let mapAspect = 1.0 / cos(degtoRad);
let latSize = 200; //
let lonSize = latSize * mapAspect; //
let latSpacing = latSize / 30.0;
let lonSpacing = lonSize / 30.0;
let offset = int(random(0, 100));
gridOverlay.fill(80);
gridOverlay.textSize(30);
gridOverlay.push();
gridOverlay.strokeWeight(0.8);
for (let i = 0; i < 6; i++) {
gridOverlay.stroke(20);
gridOverlay.line(i * latSize, 0, i * latSize, height); // longitudinal lines
gridOverlay.line(0, i * lonSize + offset, width, i * lonSize + offset); // lines of latitude
for (let j = 0; j < width; j += latSpacing) {
gridOverlay.line(j, i * lonSize + offset, j, i * lonSize - 5 + offset);
// if (j%(latSpacing*10)==0) circle (j,i * lonSize,5);
}
for (let j = 0; j < height; j += lonSpacing) {
gridOverlay.line(i * latSize, j, i * latSize - 5, j);
}
if (i % 2 == 0) {
// degrees of latitude
gridOverlay.noStroke();
gridOverlay.fill(50);
gridOverlay.text(latitudeD + "°", i * latSize + 40, 0 + 24 + offset);
gridOverlay.text(
latitudeD - 1 + "°",
i * latSize + 40,
2 * lonSize + 24 + offset
);
// degrees of longitude
gridOverlay.push();
gridOverlay.noStroke();
gridOverlay.translate(latSize * 2 + 24, i * lonSize + 90);
gridOverlay.rotate(PI / -2);
gridOverlay.text(longitudeD + 0 + "°", 0, 0);
gridOverlay.pop();
gridOverlay.push();
gridOverlay.noStroke();
gridOverlay.translate(0 + 24, i * lonSize + 90);
gridOverlay.rotate(PI / -2);
gridOverlay.text(longitudeD + 1 + "°", 0, 0);
gridOverlay.pop();
}
}
gridOverlay.pop();
print("Map Aspect ratio at " + latitudeD + " degrees: " + mapAspect);
print("latLon completed");
}
function mapIcons(refX, refY, lowX, lowY, highestX, highestY) {
iconOverlay.textAlign(CENTER);
iconOverlay.textFont("American Typewriter");
//Map noise reference point
iconOverlay.push();
iconOverlay.translate(refX, refY);
iconOverlay.noFill();
iconOverlay.strokeWeight(0.5);
iconOverlay.stroke(0, 10);
iconOverlay.circle(0, 0, 24);
iconOverlay.line(0, -24, 0, 24);
iconOverlay.line(-24, 0, 24, 0);
iconOverlay.pop();
// Map Highest Elevation
iconOverlay.noFill();
iconOverlay.strokeWeight(2);
iconOverlay.stroke(30);
iconOverlay.triangle(
highestX,
highestY,
highestX - 20,
highestY + 30,
highestX + 20,
highestY + 30
);
//Map Lowest Elevation
//circle(lowX, lowY, 10);
//iconOverlay.triangle(lowX, lowY, lowX - 20, lowY - 30, lowX + 20, lowY - 30);
//Map Compass
iconOverlay.push();
//stroke(80);
//strokeWeight(2);
iconOverlay.translate(0.2 * width, 0.2 * height);
iconOverlay.textSize(36);
iconOverlay.fill(30);
iconOverlay.noStroke();
iconOverlay.text("N", 0, -16);
iconOverlay.fill(240);
iconOverlay.triangle(0, 0, -10, 40, 0, 30);
iconOverlay.fill(50);
iconOverlay.triangle(0, 0, 10, 40, 0, 30);
iconOverlay.textSize(32);
iconOverlay.fill(200);
iconOverlay.stroke(200);
iconOverlay.text("Peninsula",2, 72);
iconOverlay.text("of", 2, 92);
iconOverlay.text("Union & Fragmentation", 2, 112);
iconOverlay.fill(10);
iconOverlay.noStroke();
iconOverlay.text("Peninsula",0, 70);
iconOverlay.text("of", 0, 90);
iconOverlay.text("Union & Fragmentation", 0, 110);
iconOverlay.pop();
let name = int(random(0, mtName.length));
// shading
iconOverlay.textSize(24);
iconOverlay.fill(200);
iconOverlay.stroke(200);
iconOverlay.text("Mt. " + mtName[name], highestX + 21, highestY + 1-5);
print(mtName[name]);
//
iconOverlay.fill(50);
iconOverlay.noStroke();
iconOverlay.text("Mt. " + mtName[name], highestX + 20, highestY-5); // +elevation ft
print(mtName[name]);
// border
iconOverlay.noFill();
iconOverlay.stroke(240);
iconOverlay.strokeWeight(40);
iconOverlay.square(0, 0, width);
iconOverlay.stroke(30);
iconOverlay.strokeWeight(1);
iconOverlay.square(20, 20, width - 40);
iconOverlay.square(15, 15, width - 30);
// slider info
iconOverlay.textSize(16);
iconOverlay.fill(0);
iconOverlay.noStroke();
iconOverlay.text("Level of Detail", 100, height - 3);
iconOverlay.text("Union - Fragmentation ", 300, height - 3);
iconOverlay.text("Unmapped ", 500, height - 3);
}