Update GUI
This commit is contained in:
parent
a3c03cafef
commit
c84a3b46b7
15 changed files with 60 additions and 52 deletions
|
|
@ -16,10 +16,6 @@ It is important to prevent ants from following pheromone trails left by those an
|
||||||
|
|
||||||
Pheromone trails left by ants evaporate and diffuse over time.
|
Pheromone trails left by ants evaporate and diffuse over time.
|
||||||
|
|
||||||
## Compute pipeline overview
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- https://softologyblog.wordpress.com/2020/03/21/ant-colony-simulations/
|
- https://softologyblog.wordpress.com/2020/03/21/ant-colony-simulations/
|
||||||
|
|
|
||||||
43
src/App.ts
43
src/App.ts
|
|
@ -21,10 +21,9 @@ export default new class App {
|
||||||
private renderer: Renderer = new Renderer(<HTMLCanvasElement>document.getElementById('canvas'));
|
private renderer: Renderer = new Renderer(<HTMLCanvasElement>document.getElementById('canvas'));
|
||||||
private scenes: SceneCollection;
|
private scenes: SceneCollection;
|
||||||
private gui: GUI = new GUI();
|
private gui: GUI = new GUI();
|
||||||
private renderLoop = (deltaTime: number): void => this.render(deltaTime);
|
private renderLoop = (time: number): void => this.render(time);
|
||||||
private simInterval: NodeJS.Timer;
|
private lastTime: number = 0;
|
||||||
private simulationStepsPerSecond: number = 0;
|
private queuedSimSteps: number = 0;
|
||||||
private simStarted: boolean = false;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initScenes();
|
this.initScenes();
|
||||||
|
|
@ -35,9 +34,6 @@ export default new class App {
|
||||||
|
|
||||||
this.renderLoop(0);
|
this.renderLoop(0);
|
||||||
|
|
||||||
this.simulationStepsPerSecond = Config.simulationStepsPerSecond;
|
|
||||||
this.updateSimulationInterval();
|
|
||||||
|
|
||||||
this.gui.on('antsCount', () => {
|
this.gui.on('antsCount', () => {
|
||||||
this.resetRenderer();
|
this.resetRenderer();
|
||||||
});
|
});
|
||||||
|
|
@ -45,27 +41,12 @@ export default new class App {
|
||||||
this.gui.on('worldSize', () => {
|
this.gui.on('worldSize', () => {
|
||||||
this.resetRenderer();
|
this.resetRenderer();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.gui.on('simulationStepsPerSecond', () => {
|
|
||||||
this.simulationStepsPerSecond = Config.simulationStepsPerSecond;
|
|
||||||
this.updateSimulationInterval();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private resetRenderer() {
|
private resetRenderer() {
|
||||||
this.renderer.reset(this.scenes);
|
this.renderer.reset(this.scenes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateSimulationInterval() {
|
|
||||||
clearInterval(this.simInterval);
|
|
||||||
|
|
||||||
this.simInterval = setInterval(() => {
|
|
||||||
this.simulationStep();
|
|
||||||
|
|
||||||
this.simStarted = true;
|
|
||||||
}, 1000 / this.simulationStepsPerSecond);
|
|
||||||
}
|
|
||||||
|
|
||||||
private initScenes() {
|
private initScenes() {
|
||||||
this.scenes = {
|
this.scenes = {
|
||||||
ants: new AntsComputeScene(this.renderer),
|
ants: new AntsComputeScene(this.renderer),
|
||||||
|
|
@ -90,19 +71,31 @@ export default new class App {
|
||||||
|
|
||||||
private simulationStep() {
|
private simulationStep() {
|
||||||
for (const scene of Object.values(this.scenes)) {
|
for (const scene of Object.values(this.scenes)) {
|
||||||
scene.update(0);
|
scene.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderer.renderSimulation(this.scenes);
|
this.renderer.renderSimulation(this.scenes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private render(deltaTime: number) {
|
private render(time: number) {
|
||||||
requestAnimationFrame(this.renderLoop);
|
requestAnimationFrame(this.renderLoop);
|
||||||
|
|
||||||
if (!this.simStarted) {
|
const deltaTime = time - this.lastTime;
|
||||||
|
const simStepsToDo = deltaTime / 1000 * Config.simulationStepsPerSecond;
|
||||||
|
|
||||||
|
this.queuedSimSteps += simStepsToDo;
|
||||||
|
|
||||||
|
while (this.queuedSimSteps >= 1) {
|
||||||
|
this.simulationStep();
|
||||||
|
--this.queuedSimSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderer.renderToScreen(this.scenes);
|
this.renderer.renderToScreen(this.scenes);
|
||||||
|
|
||||||
|
this.lastTime = time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
worldSize: 1024,
|
worldSize: 1024,
|
||||||
antsCount: 64 ** 2,
|
antsCount: 12,
|
||||||
simulationStepsPerSecond: 60,
|
simulationStepsPerSecond: 60,
|
||||||
scentThreshold: 0.01,
|
scentThreshold: 0.01,
|
||||||
scentFadeOutFactor: 0.998,
|
scentFadeOutFactor: 0.998,
|
||||||
|
|
|
||||||
17
src/GUI.ts
17
src/GUI.ts
|
|
@ -12,19 +12,28 @@ export default class GUI extends EventEmitter {
|
||||||
|
|
||||||
const simFolder = this.gui.addFolder('Simulation');
|
const simFolder = this.gui.addFolder('Simulation');
|
||||||
|
|
||||||
simFolder.add(Config, 'worldSize', 256, 4096).onChange(() => {
|
simFolder.add(Config, 'worldSize', 256, 4096)
|
||||||
|
.name('World size')
|
||||||
|
.step(1)
|
||||||
|
.onChange(() => {
|
||||||
this.emit('worldSize');
|
this.emit('worldSize');
|
||||||
});
|
});
|
||||||
simFolder.add(Config, 'antsCount', 1, 1e6).onChange(() => {
|
simFolder.add(Config, 'antsCount', 0, 22)
|
||||||
|
.name('Ants count 2^')
|
||||||
|
.step(1)
|
||||||
|
.onChange(() => {
|
||||||
this.emit('antsCount');
|
this.emit('antsCount');
|
||||||
});
|
});
|
||||||
simFolder.add(Config, 'simulationStepsPerSecond', 1, 500).onChange(() => {
|
simFolder.add(Config, 'simulationStepsPerSecond', 1, 500)
|
||||||
|
.name('Simulation steps per second')
|
||||||
|
.step(1)
|
||||||
|
.onChange(() => {
|
||||||
this.emit('simulationStepsPerSecond');
|
this.emit('simulationStepsPerSecond');
|
||||||
});
|
});
|
||||||
|
|
||||||
const controlsFolder = this.gui.addFolder('Controls');
|
const controlsFolder = this.gui.addFolder('Controls');
|
||||||
|
|
||||||
controlsFolder.add(Config, 'brushRadius', 1, 100);
|
controlsFolder.add(Config, 'brushRadius', 1, 100).name('Brush radius');
|
||||||
|
|
||||||
simFolder.open();
|
simFolder.open();
|
||||||
controlsFolder.open();
|
controlsFolder.open();
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export default class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private initResources() {
|
private initResources() {
|
||||||
const antTextureSize = Math.round(Math.sqrt(Config.antsCount));
|
const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount));
|
||||||
|
|
||||||
this.resources = {
|
this.resources = {
|
||||||
worldRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, {
|
worldRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, {
|
||||||
|
|
@ -139,7 +139,7 @@ export default class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset(scenes: SceneCollection) {
|
public reset(scenes: SceneCollection) {
|
||||||
const antTextureSize = Math.ceil(Math.sqrt(Config.antsCount));
|
const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount));
|
||||||
|
|
||||||
this.resources.worldRenderTarget.setSize(Config.worldSize, Config.worldSize)
|
this.resources.worldRenderTarget.setSize(Config.worldSize, Config.worldSize)
|
||||||
this.renderer.setRenderTarget(this.resources.worldRenderTarget);
|
this.renderer.setRenderTarget(this.resources.worldRenderTarget);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="info">Controls:<br/>Q - draw home cells<br/>W - draw food cells<br/>E - draw obstacle<br/>Drag and scroll to move the camera</div>
|
<div id="info">Controls:<br/>Q - draw home cells<br/>W - draw food cells<br/>E - draw obstacle<br/>R - erase<br/>Drag and scroll to move the camera</div>
|
||||||
<canvas id="canvas"></canvas>
|
<canvas id="canvas"></canvas>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,5 @@ export default abstract class AbstractScene extends THREE.Scene {
|
||||||
|
|
||||||
public abstract resize(width: number, height: number): void;
|
public abstract resize(width: number, height: number): void;
|
||||||
|
|
||||||
public abstract update(deltaTime: number): void;
|
public abstract update(): void;
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ export default class AntsComputeScene extends AbstractScene {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(deltaTime: number) {
|
public update() {
|
||||||
this.material.uniforms.uTime.value = performance.now();
|
this.material.uniforms.uTime.value = performance.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ export default class AntsDiscretizeScene extends AbstractScene {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(deltaTime: number) {
|
public update() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ export default class DrawScene extends AbstractScene {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(deltaTime: number) {
|
public update() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,8 @@ enum PointerState {
|
||||||
None,
|
None,
|
||||||
Food,
|
Food,
|
||||||
Home,
|
Home,
|
||||||
Obstacle
|
Obstacle,
|
||||||
|
Erase
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ScreenScene extends AbstractScene {
|
export default class ScreenScene extends AbstractScene {
|
||||||
|
|
@ -144,6 +145,10 @@ export default class ScreenScene extends AbstractScene {
|
||||||
this.drawMode = PointerState.Obstacle;
|
this.drawMode = PointerState.Obstacle;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'KeyR': {
|
||||||
|
this.drawMode = PointerState.Erase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -198,7 +203,7 @@ export default class ScreenScene extends AbstractScene {
|
||||||
this.renderHeight = height;
|
this.renderHeight = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(deltaTime: number) {
|
public update() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ export default class WorldBlurScene extends AbstractScene {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(deltaTime: number) {
|
public update() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ export default class WorldComputeScene extends AbstractScene {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(deltaTime: number) {
|
public update() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -23,8 +23,9 @@ void main() {
|
||||||
float id = float(gl_InstanceID);
|
float id = float(gl_InstanceID);
|
||||||
float sampleY = floor(id / dataTextureSize);
|
float sampleY = floor(id / dataTextureSize);
|
||||||
float sampleX = id - sampleY * dataTextureSize;
|
float sampleX = id - sampleY * dataTextureSize;
|
||||||
|
vec2 antDataUV = (vec2(sampleX, sampleY) + 0.5) / dataTextureSize;
|
||||||
|
|
||||||
vec4 dataSample = texture(tData, vec2(sampleX, sampleY) / dataTextureSize);
|
vec4 dataSample = texture(tData, antDataUV);
|
||||||
|
|
||||||
vec2 offset = dataSample.xy;
|
vec2 offset = dataSample.xy;
|
||||||
vec2 rotatedPosition = rotate(position.xy, -dataSample.z + PI * 0.5);
|
vec2 rotatedPosition = rotate(position.xy, -dataSample.z + PI * 0.5);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ void main() {
|
||||||
isHome = 1;
|
isHome = 1;
|
||||||
} else if (drawMode == 3.) {
|
} else if (drawMode == 3.) {
|
||||||
isObstacle = 1;
|
isObstacle = 1;
|
||||||
|
} else if (drawMode == 4.) {
|
||||||
|
isFood = 0;
|
||||||
|
isHome = 0;
|
||||||
|
isObstacle = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue