Skip to content

Commit 68f9ce1

Browse files
committed
UI ready
1 parent c725adb commit 68f9ce1

File tree

5 files changed

+179
-118
lines changed

5 files changed

+179
-118
lines changed

schelling_model/Boid.js

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
class Boid {
22
constructor(type, i, j) {
33
this.type = type;
4+
if (this.type == 1) {
5+
this.color = "#ff0000";
6+
}
7+
else if (this.type == 2) {
8+
this.color = "#0000ff";
9+
}
10+
else if (this.type == 3) {
11+
this.color = "#00ff00";
12+
}
13+
else if (this.type == 4) {
14+
this.color = "#ffffff";
15+
}
416

517
this.i = i;
618
this.j = j;
@@ -17,10 +29,7 @@ class Boid {
1729
this.target_i = i;
1830
this.target_j = j;
1931

20-
// vacate current cell
2132
grid[this.i][this.j] = 0;
22-
23-
// reserve target cell
2433
grid[this.target_i][this.target_j] = -1;
2534
}
2635
update() {
@@ -49,13 +58,7 @@ class Boid {
4958
}
5059
}
5160
render() {
52-
if (this.type == 1) {
53-
context.fillStyle = "#ff0000";
54-
}
55-
else if (this.type == 2) {
56-
context.fillStyle = "#0000ff";
57-
}
58-
61+
context.fillStyle = this.color;
5962
context.beginPath();
6063
context.arc(this.x, this.y, boid_radius, 0, 2 * Math.PI);
6164
context.fill();

schelling_model/basic.js

+36-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,20 @@ if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(naviga
1212
let canvas = document.getElementById("canvas");
1313
let context = canvas.getContext("2d");
1414

15+
let pause_button = document.getElementById("pause-button");
16+
17+
let unhappy_display = document.getElementById("unhappy-display");
18+
let occupancy_display = document.getElementById("occupancy-display");
19+
let num_display = document.getElementById("num-display");
20+
21+
let tol_display = document.getElementById("tol-display");
22+
let tol_input = document.getElementById("tol-input");
23+
1524
if (mobile) {
1625
canvas_width = 0.9 * screen_width;
1726
}
1827
else {
19-
canvas_width = 0.4 * screen_width;
28+
canvas_width = 0.45 * screen_width;
2029
}
2130
canvas_height = canvas_width;
2231

@@ -45,7 +54,9 @@ window.onload = function () {
4554
}
4655

4756
function defaultParams() {
48-
57+
setOccupancy(0.5);
58+
setNumber(2);
59+
tol_input.value = 0.5;
4960
}
5061

5162
let click_x, click_y, pressed;
@@ -123,4 +134,27 @@ function getTouchPosition(canvas, event) {
123134
var rect = canvas.getBoundingClientRect();
124135
click_x = event.touches[0].clientX - rect.left;
125136
click_y = event.touches[0].clientY - rect.top;
137+
}
138+
139+
function pauseToggle() {
140+
if (paused) {
141+
paused = false;
142+
pause_button.innerHTML = "Pause";
143+
}
144+
else {
145+
paused = true;
146+
pause_button.innerHTML = "Resume";
147+
}
148+
}
149+
150+
function setOccupancy(frac) {
151+
occupancy = frac;
152+
occupancy_display.innerHTML = `Occupancy: ${Math.round(occupancy * 100)}%`;
153+
initParams();
154+
}
155+
156+
function setNumber(num) {
157+
num_species = num;
158+
num_display.innerHTML = `Number of Species: ${num_species}`;
159+
initParams();
126160
}

schelling_model/simulation.html

+28-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<script src="../helper.js" defer></script>
1515
<script src="Boid.js" defer></script>
1616
<script src="basic.js" defer></script>
17+
<script src="utils.js" defer></script>
1718
<script src="user_input.js" defer></script>
1819
<script src="simulation.js" defer></script>
1920

@@ -56,7 +57,33 @@ <h2>Schelling's Model</h2>
5657
<canvas id="canvas"></canvas>
5758
</div>
5859
<div class="col s12 l4">
59-
60+
<center>
61+
<div style="background: black; border: 1px solid red; padding: 5px;">
62+
<b>
63+
Warning: <br>
64+
Do not introduce too many species with high occupancy and low tolerance. Simulation WILL hang.
65+
</b>
66+
</div>
67+
<hr>
68+
<p id="unhappy-display"></p>
69+
<button class="btn purple darken-4" id="pause-button" onclick="pauseToggle()">Pause</button>
70+
<button class="btn purple darken-4" id="restart-button" onclick="initParams()">Restart</button>
71+
<hr>
72+
<p id="occupancy-display"></p>
73+
<button class="btn purple darken-4" onclick="setOccupancy(0.25)">25%</button>
74+
<button class="btn purple darken-4" onclick="setOccupancy(0.5)">50%</button>
75+
<button class="btn purple darken-4" onclick="setOccupancy(0.75)">75%</button>
76+
<hr>
77+
<p id="tol-display"></p>
78+
<input id="tol-input" type="range" min="0.3" max="0.8" step="0.05" oninput="updateParams('tol')"
79+
onchange="updateParams('tol')">
80+
<span>(Lower tolerance implies greater segregation)</span>
81+
<hr>
82+
<p id="num-display"></p>
83+
<button class="btn purple darken-4" onclick="setNumber(2)">2</button>
84+
<button class="btn purple darken-4" onclick="setNumber(3)">3</button>
85+
<button class="btn purple darken-4" onclick="setNumber(4)">4</button>
86+
</center>
6087
</div>
6188
</div>
6289
</div>

schelling_model/simulation.js

+18-105
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
let num_cells, cell_length, boid_radius;
22

3-
let occupancy = 0.5;
3+
let num_species = 4;
44

5-
let tolerance = 0.33;
5+
let occupancy = 0.5, tolerance = 0.33;
66

7-
let boids;
8-
let grid;
7+
let boids, grid;
98

109
function update() {
1110
let num_unhappy_boids = getNumUnhappyBoids();
1211

12+
unhappy_display.innerHTML = `Unhappiness: ${(num_unhappy_boids / getNumBoids() * 100).toFixed(2)}%`;
13+
1314
if (num_unhappy_boids > 0) {
1415
while (true) {
1516
let i = Math.floor(Math.random() * num_cells);
@@ -32,10 +33,6 @@ function update() {
3233
for (let boid of boids) {
3334
boid.update();
3435
}
35-
36-
let num_boids = getNumBoids();
37-
38-
// console.log(num_boids, num_unhappy_boids);
3936
}
4037

4138
function render() {
@@ -50,27 +47,28 @@ function render() {
5047
}
5148

5249
function drawUnhappySquares() {
53-
context.fillStyle = "#aaaaaa";
50+
context.strokeStyle = "#ffffff";
5451

5552
for (let i = 0; i < num_cells; i++) {
5653
for (let j = 0; j < num_cells; j++) {
5754
if (grid[i][j] > 0) {
5855
let fraction = getFraction(i, j, grid[i][j]);
5956

6057
if (fraction > tolerance) {
61-
context.fillRect((j + 0.1) * cell_length, (i + 0.1) * cell_length, 0.8 * cell_length, 0.8 * cell_length);
62-
63-
// context.beginPath();
64-
// context.rect((j + 0.1) * cell_length, (i + 0.1) * cell_length, 0.8 * cell_length, 0.8 * cell_length);
65-
// context.stroke();
58+
context.beginPath()
59+
context.rect(j * cell_length, i * cell_length, cell_length, cell_length);
60+
context.stroke();
6661
}
6762
}
6863
}
6964
}
7065
}
7166

7267
function updateParams(variable) {
73-
68+
if (variable == "tol") {
69+
tolerance = tol_input.value;
70+
tol_display.innerHTML = `Tolerance: ${tolerance}`;
71+
}
7472
}
7573

7674
function initObjs() {
@@ -85,13 +83,7 @@ function initObjs() {
8583
let num_boids = Math.floor(num_cells * num_cells * occupancy);
8684

8785
for (let i = 0; i < num_boids; i++) {
88-
let type = 0;
89-
if (i % 2 == 0) {
90-
type = 1;
91-
}
92-
else {
93-
type = 2;
94-
}
86+
let type = Math.floor(Math.random() * num_species) + 1;
9587

9688
while (true) {
9789
let i = Math.floor(Math.random() * num_cells);
@@ -120,92 +112,13 @@ function initParams() {
120112
cell_length = canvas_width / num_cells;
121113
boid_radius = 0.4 * cell_length;
122114

123-
initObjs();
124-
}
125-
126-
function getHappySpotFor(i, j) {
127-
while (true) {
128-
let i_new = Math.floor(Math.random() * num_cells);
129-
let j_new = Math.floor(Math.random() * num_cells);
130-
131-
if (grid[i_new][j_new] == 0) {
132-
let fraction = getFraction(i_new, j_new, grid[i][j]);
133-
134-
if (fraction <= tolerance) {
135-
let boid = getBoidAt(i, j);
136-
boid.moveTo(i_new, j_new);
137-
138-
console.log(i, j, "moving to", i_new, j_new);
139-
break;
140-
}
141-
}
142-
}
143-
}
144-
145-
function getBoidAt(i, j) {
146-
for (let boid of boids) {
147-
if (!boid.moving && boid.i == i && boid.j == j) {
148-
return boid;
149-
}
150-
}
151-
}
152-
153-
function getFraction(i, j, type) {
154-
let num_total = 0, num_opp = 0;
155-
156-
for (let i_off = -1; i_off <= 1; i_off++) {
157-
for (let j_off = -1; j_off <= 1; j_off++) {
158-
let i_index = i + i_off;
159-
let j_index = j + j_off;
160-
161-
if (i_index < 0) {
162-
i_index = num_cells - 1;
163-
}
164-
else if (i_index == num_cells) {
165-
i_index = 0;
166-
}
167-
168-
if (j_index < 0) {
169-
j_index = num_cells - 1;
170-
}
171-
else if (j_index == num_cells) {
172-
j_index = 0;
173-
}
174-
175-
if (grid[i_index][j_index] > 0) {
176-
num_total++;
177-
178-
if (grid[i_index][j_index] != type) {
179-
num_opp++;
180-
}
181-
}
182-
}
115+
if (paused) {
116+
pauseToggle();
183117
}
184118

185-
if (num_total == 0) {
186-
return 1;
187-
}
188-
else {
189-
return num_opp / num_total;
190-
}
191-
}
119+
updateParams("tol");
192120

193-
function getNumUnhappyBoids() {
194-
let num_unhappy_boids = 0;
195-
196-
for (let i = 0; i < num_cells; i++) {
197-
for (let j = 0; j < num_cells; j++) {
198-
if (grid[i][j] > 0) {
199-
let fraction = getFraction(i, j, grid[i][j]);
200-
201-
if (fraction > tolerance) {
202-
num_unhappy_boids++;
203-
}
204-
}
205-
}
206-
}
207-
208-
return num_unhappy_boids;
121+
initObjs();
209122
}
210123

211124
function getNumBoids() {

0 commit comments

Comments
 (0)