read more here: https://github.com/arjunmakesthings/daily-movement_emergence-studies
log:
260619:
mass had to grow with age, but then slow down. tried logarithmic growth, but that wasn’t proper (as growth needs to have a ceiling). could have used constrain, but that’s unnecessary computation.
found this: https://math.stackexchange.com/questions/2821035/exponential-something-what-is-the-name-of-that-asymptotic-exponential-functi (thanks to jagi).

equation:
which can be implemented like so:
const a = 100; //max.
const b = 50; //how quickly max is achieved.
const base = 50; //to ensure base of graph doesn't shift.
y = base + a * (1 - Math.exp(-(x - base) / b));plotted like this:

260625:
got these getters right:
get_mass() {
//for a given current age, calculate mass.
//mass is an asymptotic-exponential-growth graph.
const a = 10; //max.
const b = 18; //in how many steps is max achieved.
return (a * (1 - Math.exp(-5 * (this.curr_age / b)))) / (1 - Math.exp(-5));
}
get_energy() {
//energy is like a bell curve.
/*
https://www.desmos.com/calculator/3iioyvma2l
f(x) = y = e^(-((x-a)^2) / b).
*/
const m = 10;
return (
(1 / (1 + Math.exp(-(this.curr_age - 18) / 2))) *
(1 / (1 + Math.exp((this.curr_age - 35) / 5))) *
m
);
}the simulation works well. need to refactor a little bit to clean it up, and make things more variable to have a basic simulation going.
fixed scheduling:
/*
for given age, get a schedule based on the busyness (the older you are, the more busy you get).
*/
get_schedule(age) {
let avl_time = Array.from({ length: day_length }, (_, i) => i);
let schedule = [];
//calculate available time slots based on age.
const min = 2;
const max = 12;
const peak = 25;
const sigma = 10; //spread-width.
const g = Math.exp(-Math.pow(age - peak, 2) / (2 * sigma * sigma));
const mean_busyness = min + (max - min) * g;
const spread = 1.25 + (1 - g) * 3.5;
let busyness = Math.round(randomGaussian(mean_busyness, spread));
busyness = constrain(busyness, min, max);
//^ this is the length of our to-be-created schedule array.
let i = 0;
while (i < busyness) {
i = (i + Math.floor(Math.random() * 6)) % avl_time.length;
let a = avl_time.splice(i, 1)[0],
b = avl_time.splice(i, 1)[0];
schedule.push(
schedule.length ? [schedule[schedule.length - 1][1], a] : [a, b],
);
}
schedule.push([schedule[schedule.length - 1][1], schedule[0][0]]);
this.get_new_destinations(schedule);
return schedule;
}wrote a small algorithm distribute points in space — somewhat randomly — but with a minimum distance between them.
was inspired by the poisson-disk-sampling algorithm.
but mine is simpler.
/*
select hotspots away from each other, recursively, by drawing circles.
*/
function setup() {
createCanvas(800, 800);
background(255);
// let spots = find_spots(width, height, 5, 20);
let spots = find_spots(width, height, 5, 100);
// console.log(spots);
}
/*
for a canvas of width w & height h, and a min-radius specified between points,
return tuples of x, y coordinates such that;
coordinates are away by min-radius,
and are somewhat uniformally distributed across space.
*/
function find_spots(w, h, n, min_spacing) {
let posis = Array.from({ length: n }, () => []);
let size = min_spacing;
//check if co-centric circles min_spacing apart can fit onto the space:
const can_fit = Math.floor(Math.min(w, h) / min_spacing) >= n;
//we now do two branches:
const origin = createVector(width / 2, height / 2);
if (can_fit) {
//randomly plot them on the circles (they will always be at-least min-spacing apart).
for (let i = 0; i < n; i++) {
let theta = random(TWO_PI);
let x = origin.x + (size / 2) * cos(theta);
let y = origin.y + (size / 2) * sin(theta);
noFill();
stroke(0);
strokeWeight(0.5);
// circle(origin.x, origin.y, size);
strokeWeight(10);
stroke(255, 0, 0);
point(x, y);
size += min_spacing;
}
} else {
let circle_count = 0;
while (origin.x + size / 2 < w && origin.y + size / 2 < h) {
noFill();
stroke(0);
strokeWeight(0.5);
circle(origin.x, origin.y, size);
size += min_spacing;
circle_count++;
}
let spots_on_each = Math.ceil(n / circle_count);
//reset size:
size = min_spacing;
let drawn = 0;
for (let i = 0; i < circle_count; i++) {
//draw the original circle:
noFill();
stroke(0);
strokeWeight(0.5);
// circle(origin.x, origin.y, size);
let start_theta = random(TWO_PI);
let inc = TWO_PI / circle_count;
let theta = start_theta;
for (let j = 0; j < spots_on_each && drawn < n; j++) {
let x = origin.x + (size / 2) * cos(theta);
let y = origin.y + (size / 2) * sin(theta);
stroke(255, 0, 0);
strokeWeight(10);
point(x, y);
theta += inc;
drawn++;
}
size += min_spacing;
}
}
return posis;
}
explanation:
given a 2-d surface of dimensions {w,h},
we draw cocentric circles from an origin, w units apart (w == min spacing required between points) until:
a) # of concentric circles == n (number of points we want)
b) cocentric circle x + r >= width or height of surface.
in case (a):
we select a random theta, and draw a point for each of the circles so that:
x = origin + size/2 * cos(theta)
y = origin + size/2 * sin(theta)
in case (b)*
for each co-centric circle, we draw multiple points d degrees apart such that:
d == n (number of points we wanted) / # of cocentric circles.
* in case b, it is not possible (with this approach) to have n points w units apart. so, we do the best we can.i considered returning vectors and not tuples, so that you could access, say, spots[n].x && spots[n].y, but decided against it (more operation required). in this case, an array works better.
260721:
wip pictures from interpretation 1:
.jpeg)
260722:
was walking & thought of all the ‘missed connections’ we have.
/*
interpretation #2.0: missed connections.
thought:
we walk by so many people. when they are within a certain distance, we have a short window of time to connect with each other in physical-space, until we are distant again.
expression:
draw a line from each being to other beings around them within a specific radius, with the distance between them signifying the intensity of a possible connection.
parameters:
population: 1000,
day_length: 10,
22nd july, 2026.
*/
/*
interpretation #2.1: missed connections.
thought:
we walk by so many people. when they are within a certain distance, we have a short window of time to connect with each other in physical-space, until we are distant again.
expression:
draw a line from each being to other beings around them within a specific radius, with the distance between them signifying the intensity of a possible connection, over time. connections are wiped out, however, by the movement of beings.
parameters:
population: 1000,
day_length: 10,
22nd july, 2026.
*/

260724:
achieved delaunay triangulation:

interpretation 3:
/*
interpretation #3: all the world's a mesh.
thought:
all beings are connected to each other via an invisible mesh.
expression:
treat the position of each being as a vertex. generate a triangular-mesh by finding tuples close to each other, so that no point is inside a face. keep generating the mesh over time, as beings move.
parameters:
population: 1000
day-length: 5
24th july, 2026.
*/


260727:
/*
interpretation #4.1: meetings are ripples.
thought:
meeting people can be resounding. when two beings meet, a ripple is sent through in space & time; that may affect other beings.
expression:
connect beings close to each other with a black line. when two beings collide, send out a circular ripple expanding from the point of collision. the closer the beings are, the further the ripple goes. lay this out over time.
parameters:
population: 500
day-length: 10
27th july, 2026.
*/

260730:
/*
interpretation #1.3: born in pairs.
thought:
beings are destined to be with someone that they were born close (in proximity & age) to. however, as they live out their lives, they may be close or separated in the world. ideology borrowed from: red thread of fate -> https://en.wikipedia.org/wiki/Red_thread_of_fate.
expression:
draw a line between a being & their partner over time. the closer they are, the darker the line; the further they are, the lighter the line.
parameters:
population = 40;
day length = 10;
30th july, 2026.
*/
