Commit 2baeffaa authored by Nemecle's avatar Nemecle
Browse files

Initial commit

parents
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
<link rel="stylesheet" href="https://cd.nemecl.eu/css/base.css" type="text/css" />
</head>
<body>
<header>
<span class = "sitename"><a href="https://nemecl.eu/">nemecle.eu<strong></strong></a></span>
<nav class = "sitenav" aria-label="global domain navigation">
<a href = "https://nemecl.eu">home</a>
<a href = "https://txt.nemecl.eu" class = "block" >txt</a>
<a href = "https://art.nemecl.eu">art</a>
<a href = "https://wiki.nemecl.eu">wiki</a>
</nav>
</header><!-- /#banner -->
<script src="utilities.js"></script>
<script src="line_breaking.js"></script>
<script src="line_alphabet.js"></script>
<script src="transport.js"></script>
<script src="sketch.js"></script>
<main>
<h1>The map is not the territory</h1>
<nav aria-label="controls">
<a href = "index.html">[random]</a>
<a id = "direct-link" href = "index.html?city=">[Link to this map]</a>
</nav>
<div id = "canvas">
</div>
<h2>About</h2>
<p>This map is procedurally generated (created using code) with <a href = "p5js.org/">p5*js</a>. It generates a random population map, creates a set of public transport types and tries to link the mot populated areas and tries to link them using this network.
</main>
</body>
</html>
function convert_to_base(n, base) {
return n;
}
class Alphabet {
constructor() {
let alphabets = [
"abcdefghiklmnopqrstuwxyz",
"ABCDEFGHIKLMNOPQRSTUWXYZ",
"123456789",
"123456789ABCDEF",
"☉☽︎☿♀♁🜨♂♃♄⛢⧬♅♆⯉⚳⚴⚵⚶⚘♇",
"ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ",
"αβγδεζηθικλμνξοπρστυφχψω"
];
this.alphabet = pick(alphabets);
}
get_id (index) {
let base = this.alphabet.length -1;
//print(`(alphabet) [${this.alphabet}] i: ${index} / ${base} -> ${this.alphabet[(index % base)]}`)
return this.alphabet[(index % base) ];
}
}
\ No newline at end of file
let DEBUG = false;
function random_split_length (distance, minimum) {
let total = 0;
let splitted = [];
let watchdog = 0;
let signed_minimum = Math.sign(distance) * minimum;
while (total != distance) {
let segment = floor(random (signed_minimum, distance));
if(DEBUG) {print("random segment: ", segment)}
total = sum_array(splitted);
if(DEBUG) {print("current total: ", total, splitted);}
if (abs(segment + total) > abs(distance)) {
if(DEBUG) {print("segment too large! (dist: ", distance," total: ", total," segment: ", segment, ")");}
segment = distance - total;
if(DEBUG) {print("setting segment to ", segment);}
}
if(abs(segment) >= minimum) {
splitted.push(segment);
total += segment;
} else {
if(DEBUG) {print("segment too short! ", segment, " for min ", minimum);}
}
watchdog +=1;
}
if(DEBUG) {print("splitted end: ", splitted);}
return splitted;
}
function get_broken_line (p1, p2, minimum=1, diagonals=false) {
let points = [];
// sign agnostic
let x_distance = p2.x - p1.x;
let y_distance = p2.y - p1.y;
let x_minimum = Math.sign(x_distance) * minimum;
let y_minimum = Math.sign(y_distance) * minimum;
if(DEBUG) {print("from ", p1.x, p1.y, " to ", p2.x, p2.y, "(", x_distance, y_distance, ")");}
if(DEBUG) {print("----------------line X")}
let x_split = random_split_length(x_distance, minimum); // welcome to my stream
if(DEBUG) {print("----------------line Y")}
let y_split = random_split_length(y_distance, minimum);
points.push(p1);
//watchdog = 0; // 1000 loops overall!
let watchdog = 0;
let total_x = 0;
let total_y = 0;
while((x_split.length != 0 || y_split.length != 0) && watchdog < 1000) {
let vertical_or_horizontal = random();
if (vertical_or_horizontal > 0.5) {
// https://stackoverflow.com/questions/36069870/how-to-remove-random-item-from-array-and-then-remove-it-from-array-until-array-i
let x_segment = Number(x_split.splice(floor(random()*x_split.length), 1));
total_x += x_segment;
if(DEBUG) {print("picked for x: ", x_segment, ". Still ", x_split.length, " elements.")}
let next_point = points[points.length-1].copy();
let offset = createVector(x_segment,0);
next_point = p5.Vector.add(next_point, offset);
points.push(next_point);
} else {
// https://stackoverflow.com/questions/36069870/how-to-remove-random-item-from-array-and-then-remove-it-from-array-until-array-i
let y_segment = Number(y_split.splice(floor(random()*y_split.length), 1));
total_y += y_segment;
if(DEBUG) {print("picked for y: ", y_segment, ". Still ", y_split.length, " elements.")}
let next_point = points[points.length-1].copy();
let offset = createVector(0, y_segment);
next_point = p5.Vector.add(next_point, offset);
points.push(next_point);
}
watchdog += 1;
}
if(DEBUG) {print("END ", total_x,total_x)}
if (watchdog > 1000) {
if(DEBUG) {print("(get_broken_line) WARNING: watchdog triggered (> 1000 loops)")}
}
return points;
}
function display_broken_line (broken_line, line_drawing_callback=line) {
/* requires to set stroke and strokeWeight before calling */
/*for (let s = 0; s < broken_line.length; s++) {
point(broken_line[s].x * GRID_SCALE,broken_line[s].y * GRID_SCALE);
}*/
for (let s = 0; s < broken_line.length -1 ; s++) {
let s1 = broken_line[s];
let s2 = broken_line[s+1];
//strokeWeight(5);
line_drawing_callback(s1.x * GRID_SCALE, s1.y * GRID_SCALE,s2.x * GRID_SCALE, s2.y * GRID_SCALE)
}
}
function display_double_broken_line(broken_line, begin_x,begin_y, end_x,end_y, external_size=ENV_EXTERNAL_STROKE, external_colour=ENV_EXTERNAL_COLOUR, internal_size=ENV_INTERNAL_STROKE, internal_colour=ENV_INTERNAL_COLOUR) {
/* begin and end must be 2d vectors */
/* requires to set stroke and strokeWeight before calling */
/*for (let s = 0; s < broken_line.length; s++) {
point(broken_line[s].x * GRID_SCALE,broken_line[s].y * GRID_SCALE);
}*/
for (let s = 0; s < broken_line.length -1 ; s++) {
let s1 = broken_line[s];
let s2 = broken_line[s+1];
//strokeWeight(5);
strokeWeight(external_size);
stroke(external_colour);
line(s1.x * GRID_SCALE, s1.y * GRID_SCALE,s2.x * GRID_SCALE, s2.y * GRID_SCALE)
}
for (let s = 0; s < broken_line.length -1 ; s++) {
let s1 = broken_line[s];
let s2 = broken_line[s+1];
//strokeWeight(5);
strokeWeight(internal_size);
stroke(internal_colour);
line(s1.x * GRID_SCALE, s1.y * GRID_SCALE,s2.x * GRID_SCALE, s2.y * GRID_SCALE)
}
}
\ No newline at end of file
let pos = 0;
var WIDTH = 500 // -1
var HEIGHT = WIDTH;
var TOTAL_LEGEND_HEIGHT;
var NOISE_SEED;
var RANDOM_SEED;
var NOISE_LOD = 8;
var NOISE_FALLOFF = 0.50;
var NOISE_SCALE;
var GRID_SCALE;
var COAST_VALUE = 0.7;
var POPULATION = [];
var POPULATION_RANKED;
var POPULATION_SIZE;
var name;
var IS_SF = false;
var IS_MAGIC = false;
var ENV_INTERNAL_STROKE;
var ENV_INTERNAL_COLOUR;
var ENV_EXTERNAL_STROKE;
var ENV_EXTERNAL_COLOUR;
var TRANSPORTS = [];
// schemes = [bg_colour, fg_colour];
var DARK_OLD_SCHOOL_SF_THEME = {
background: "black",
foreground: "white"
};
var MODERN_THEME = {
background: 240,
foreground: [53, 61, 69]
};
var LIGHT_MAGIC_THEME = {
background: [240, 232, 204],
foreground: [89, 85, 72]
};
var DARK_FANTASY_THEME = {
background: [53, 61, 69],
foreground: "white"
};
var THEME;
var GENERAL_FONT;
var SHOW_INDIVIDUAL_LINES = false;
var scale_slider;
var DRAW_SCALE = 1;
var x_offset = 0;
var y_offset = 0;
var INTERACTIVE = false;
class Cell {
constructor(x,y, population=0) {
this.x = x;
this.y = y;
this.population = population;
}
display() {
strokeWeight(this.population);
point(this.x * GRID_SCALE, this.y * GRID_SCALE);
}
}
function get_noise(x,y) {
return (noise(x * NOISE_SCALE, y * NOISE_SCALE)) * 1.9 - 0.30;
}
function preload() {
GENERAL_FONT = loadFont("inconsolata-v20-latin-ext_latin-regular.ttf");
}
function setup() {
let seed = find_get_parameter("city");
if(seed == null || seed == "") {
seed = random(0,10000);
name = get_random_city_name(int(random(2,6)));
} else {
name = seed;
}
let nbr_seed = get_number_from_string(name);
print(`"${name}" will be used as seed, giving numeric seed ${nbr_seed}`)
RANDOM_SEED = NOISE_SEED = nbr_seed;
noiseSeed(NOISE_SEED)
randomSeed(RANDOM_SEED);
var direct_link = document.getElementById('direct-link');
direct_link.setAttribute('href', `index.html?city=${name}`);
POPULATION_SIZE = int(random(10000, 50000));
let number_of_transports = int(random(2,6));
print(`${number_of_transports} different transports`)
if(SHOW_INDIVIDUAL_LINES) {
TOTAL_LEGEND_HEIGHT = 55; //8 * GRID_SCALE;
} else {
TOTAL_LEGEND_HEIGHT = 25; //5 * GRID_SCALE;
}
if(INTERACTIVE || WIDTH == -1) {
//https://www.reddit.com/r/processing/comments/boyl8n/windowwidth_and_windowheight_sometimes_results_in/
const css = getComputedStyle(canvas.parentElement),
marginWidth = round(float(css.marginLeft) + float(css.marginRight)),
marginHeight = round(float(css.marginTop) + float(css.marginBottom)),
w = windowWidth - marginWidth, h = windowHeight - marginHeight;
WIDTH = w // windowWidth;
HEIGHT = h - (number_of_transports) * TOTAL_LEGEND_HEIGHT;
}
//noiseDetail(NOISE_LOD, NOISE_FALLOFF);
NOISE_SCALE = 0.005 //random(0.02, 0.01);
GRID_SCALE = WIDTH/100 //int(NOISE_SCALE * 400);
print("seed:", NOISE_SEED, " population size: ", POPULATION_SIZE, " grid scale: ", GRID_SCALE);
for(let x = 0; x < WIDTH/GRID_SCALE; x ++) {
POPULATION[x] = [];
for(let y = 0; y < HEIGHT/GRID_SCALE; y ++) {
POPULATION[x][y] = new Cell(x,y);
}
}
let p = 0;
let it = 0;
while (p < POPULATION_SIZE) {
//let x = int(random(width/GRID_SCALE));
//let y = int(random(height/GRID_SCALE));
let x = int(randomGaussian((WIDTH/GRID_SCALE)/2, 50));
let y = int(randomGaussian((HEIGHT/GRID_SCALE)/2, 50));
// gaussian noise can be quirky
if (
x > 0 && x < WIDTH/GRID_SCALE &&
y > 0 && y < HEIGHT/GRID_SCALE &&
get_noise(x * GRID_SCALE,y * GRID_SCALE) > COAST_VALUE &&
POPULATION[x][y].population < GRID_SCALE * 1.1) {
POPULATION[x][y].population+=1;
p++;
}
else {
//if as many fails as population, probably a dead run
it++;
}
// safety
if (it > POPULATION_SIZE) {
break;
}
}
POPULATION_RANKED = get_population_ranked(POPULATION);
if (random() < 0.30) {
IS_SF = true;
THEME = pick([DARK_OLD_SCHOOL_SF_THEME])
print("is SF");
}
if (random() < 0.30) {
IS_MAGIC = true;
THEME = pick([LIGHT_MAGIC_THEME, DARK_FANTASY_THEME])
print("is magic");
}
if (!IS_MAGIC && !IS_SF) {
THEME = pick([MODERN_THEME])
}
for(let i = 0; i < number_of_transports; i++) {
let nlines = random(5,30);
let rank = abs(random(10,50));
TRANSPORTS.push(new Transport(POPULATION, POPULATION_RANKED, nlines, rank));
}
//TRANSPORTS.push(new Transport(POPULATION, POPULATION_RANKED, 10, 0));
//TRANSPORTS.push(new Transport(POPULATION, POPULATION_RANKED, 30, 50));
let main_canvas = createCanvas(WIDTH, HEIGHT + (TRANSPORTS.length + 1) * TOTAL_LEGEND_HEIGHT);
main_canvas.parent("canvas");
if(!INTERACTIVE) {
noLoop();
}
}
function get_population_ranked(pop) {
let population_flat = pop.flat();
population_flat.sort(function(a, b) {
if (a.population > b.population) {
return -1;
}
if (a.population < b.population) {
return 1;
}
// population must be equal
return 0;
});
return population_flat;
}
function get_random_city_name (length = -1) {
let diphthongs = [
"ʼd", "ʼy", "aa", "ae", "ah", "ai", "", "", "ái", "ãi", "am", "âm", "an", "ân", "än", "ån", "", "ao", "ão", "aq", "au", "äu", "", "aw", "ay", "bd", "bf", "bh", "bm", "bp", "bv", "bz", "cg", "çh", "čh", "ci", "cj", "ck", "", "cr", "cs", "ct", "cu", "cw", "cx", "cz", "dd", "dg", "dh", "dl", "", "dm", "dn", "dp", "dq", "dr", "ds", "dt", "dv", "dx", "dy", "", "", "ea", "", "éa", "ee", "eh", "ei", "", "éi", "ej", "em", "ém", "êm", "en", "én", "ên", "eo", "eq", "eu", "", "ew", "êw", "ey", "fh", "fx", "gb", "gc", "ge", "gg", "gi", "gj", "gk", "gl", "gm", "gn", "go", "gq", "gr", "gu", "", "gv", "gw", "ǥw", "gx", "gy", "", "", "", "", "hj", "hl", "hm", "hn", "hr", "hs", "hu", "hv", "hx", "hy", "ia", "ie", "ig", "ih", "ii", "il", "im", "ím", "in", "ín", "în", "", "io", "ío", "iq", "iu", "", "iw", "ix", "jj", "", "jr", "jx", "kh", "kj", "kk", "kl", "km", "kn", "kp", "kr", "ks", "ku", "kv", "ḵw", "kx", "ky", "ḷḷ", "lr", "lv", "lw", "lx", "md", "mf", "mg", "mh", "ml", "mm", "mn", "mp", "mq", "mt", "mv", "mw", "mx", "nb", "nc", "nd", "nf", "ñg", "ni", "nm", "ńm", "nn", "np", "nq", "nr", "ns", "nt", "nv", "nw", "nx", "nz", "", "", "", "", "n-", "oa", "oe", "", "ôe", "õe", "oh", "oi", "", "", "ói", "òi", "om", "ôm", "on", "ôn", "ön", "oo", "oq", "or", "ou", "", "ow", "ôw", "oy", "", "øy", "ph", "pl", "pm", "pn", "pp", "pq", "ps", "pt", "pw", "py", "qh", "qk", "qo", "qq", "qv", "qw", "qy", "rh", "rl", "rm", "rn", "rp", "rr", "rs", "rt", "rw", "rz", "", "sg", "si", "sj", "sk", "sl", "sp", "sr", "ss", "st", "sv", "sx", "sy", "tf", "tg", "ti", "tj", "tk", "tl", "", "tm", "tn", "tp", "tr", "ts", "tt", "tw", "tx", "ty", "tz", "uc", "ue", "ûe", "ug", "uh", "ui", "", "úi", "um", "úm", "un", "ún", "ün", "", "uo", "uq", "ur", "uu", "uw", "uy", "ux", "vg", "vh", "vk", "vn", "vv", "vr", "wr", "wu", "ww", "wx", "xg", "xh", "xi", "xk", "", "xs", "xu", "xw", "xx", "xy", "yh", "yi", "yk", "ym", "yn", "yr", "yu", "yw", "yx", "yy", "zi", "zl", "zr", "zs", "zv", "zw", "zz", "ɔn", "œu", "ŋg", "ŋk", "ŋm", "ŋv", "ſh", "ǃʼ", "ǃg", "ǃh", "ǃk", "ǃn", "ǃx"
]
let lower_case_name = "";
if (length == -1) {
length = random(1,10);
}
for (let i = 0; i < length; i++) {
lower_case_name += pick(diphthongs);
}
let name = lower_case_name.charAt(0).toUpperCase() + lower_case_name.slice(1);
return name;
}
function draw_relief () {
for(let x = 0; x < width; x++) {
for(let y = 0; y < height; y++) {
let value = get_noise(x, y);
let pen = 0;
if (value > COAST_VALUE) {
pen = value * 50;
} else {
pen = value * 10;
}
stroke(pen);
//min_value = min(get_noise(x,y), min_value);
//max_value = max(get_noise(x,y), min_value);
point(x,y);
}
}
}
function draw_land () {
for(let x = 0; x < width; x++) {
for(let y = 0; y < height; y++) {
let value = get_noise(x, y);
let pen = 0;
if (value >= COAST_VALUE) {
pen = value * 50;
stroke([255,255,255, pen]);
point(x,y);
}
}
}
}
function draw_ocean () {
for(let x = 0; x < width; x++) {
for(let y = 0; y < height; y++) {
let value = get_noise(x, y);
let pen = 0;
if (value < COAST_VALUE) {
pen = value * 50;
stroke([0,0,0, pen]);
point(x,y);
}
}
}
}
function draw_population() {
stroke(THEME.foreground);
for(let x = 0; x < POPULATION.length; x++) {
for(let y = 0; y < POPULATION[0].length; y++) {
POPULATION[x][y].display();
}
}
}
function draw_city_name(city_name="ASTREOL") {
let text_size = 30;
let text_x = 10;
let text_y = text_size + 5;
let padding = 5;
textSize(text_size);
textStyle(BOLD);
textFont(GENERAL_FONT);
let text_w = textWidth(city_name);
let text_h = text_size;
noStroke()
fill(THEME.foreground);
rect(text_x - padding, text_y - text_h / 1.2 - padding, text_w + padding * 2, text_h + padding * 2);
noStroke()
fill(THEME.background); //fill(245, 95, 125);
text(city_name, text_x, text_y)
}
function get_network_information () {
}
function draw_legend (show_individual_lines=true) {
let top = HEIGHT;
fill(THEME.background)
noStroke();
rect(0, top, WIDTH, HEIGHT);
for (let t = 0; t < TRANSPORTS.length; t++) {
let transport = TRANSPORTS[t];
let x = 10;
let y = top + 40+ t * TOTAL_LEGEND_HEIGHT;
transport.display_legend(x,y, show_individual_lines);
}
}
function mouseWheel(event) {
if(INTERACTIVE) {
DRAW_SCALE -= event.delta/1000;
DRAW_SCALE = constrain(DRAW_SCALE, 1, 5)
if(DRAW_SCALE == 1) {
x_offset = 0;
y_offset = 0;
}
}
}
function mouseDragged() {
if(INTERACTIVE) {
if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
x_offset = mouseX - x_offset;
y_offset = mouseY - y_offset;
}
}
}
function draw() {