Skip to content

Commit

Permalink
refactor: use class
Browse files Browse the repository at this point in the history
  • Loading branch information
rei1024 committed Oct 3, 2024
1 parent 94fbcff commit 6148436
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 108 deletions.
125 changes: 125 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import * as BABYLON from "babylonjs";
import { BitGrid, BitWorld } from "@ca-ts/algo/bit";
import { $autoRandom } from "./bind";
import { createTemplateCell } from "./lib/cell";
import { setRLE } from "./lib/setRLE";

const WORLD_SIZE = 32 * 2;
const stackHeight = 1;

export class App {
private prevGrid: BitGrid | null = null;
private prevPrevGrid: BitGrid | null = null;
private bitWorld = BitWorld.make({ width: WORLD_SIZE, height: WORLD_SIZE });
private generation = 0;
public historySize = 16;
private templateMesh: BABYLON.Mesh;
private cellMaterial: BABYLON.StandardMaterial;
private cellMeshes: BABYLON.InstancedMesh[][] = [];

constructor(
private engine: BABYLON.Engine,

Check failure on line 21 in src/app.ts

View workflow job for this annotation

GitHub Actions / build

Property 'engine' is declared but its value is never read.
private scene: BABYLON.Scene,

Check failure on line 22 in src/app.ts

View workflow job for this annotation

GitHub Actions / build

Property 'scene' is declared but its value is never read.
private camera: BABYLON.ArcRotateCamera,
private pointLight: BABYLON.PointLight
) {
this.bitWorld.random();
this.initCamera();
const { templateCell, cellMaterial } = createTemplateCell(scene);
this.templateMesh = templateCell;
this.cellMaterial = cellMaterial;
}

private initCamera() {
const worldCenterX = this.bitWorld.getWidth() / 2;
const worldCenterY = this.bitWorld.getHeight() / 2;
// BitWorldの中心を計算
this.camera.target = new BABYLON.Vector3(worldCenterX, 0, worldCenterY);
}

setSize(size: number) {
this.generation = 0;
this.camera.target.y = 0;
this.pointLight.position.y = 0;
this.clearCell();
this.bitWorld = BitWorld.make({ width: size, height: size });
this.initCamera();
this.random();
}

updateWorld() {
this.prevPrevGrid = this.prevGrid;
this.prevGrid = this.bitWorld.bitGrid.clone();
this.bitWorld.next();

if (
$autoRandom.checked &&
this.prevPrevGrid &&
this.bitWorld.bitGrid.equal(this.prevPrevGrid)
) {
this.clearCell();
this.bitWorld.random();
}

const newCells: BABYLON.InstancedMesh[] = [];

// 新しい世代のセルを表示
this.bitWorld.forEach((x, y, alive) => {
if (alive === 1) {
// セルのインスタンスを作成
const instance = this.templateMesh.createInstance(`cell_${x}_${y}`);
instance.position = new BABYLON.Vector3(
x,
this.generation * stackHeight,
y
);

// マテリアルや色はテンプレートセルと共有されるので追加設定不要
newCells.push(instance);
// instance.scaling = new Vector3(0.5, 0.5, 0.5);
}
});

this.cellMeshes.push(newCells);

// 古い世代のセルを削除
if (this.cellMeshes.length >= this.historySize) {
const old = this.cellMeshes.slice(
0,
this.cellMeshes.length - this.historySize
);
old.forEach((a) => {
a.forEach((c) => c.dispose());
});
this.cellMeshes = this.cellMeshes.slice(
this.cellMeshes.length - this.historySize
);
}
// カメラを移動
this.camera.target.y += stackHeight;
// 点光源の位置を移動させる
this.pointLight.position.y += stackHeight;
this.generation++;
}

clearCell() {
this.cellMeshes.forEach((a) => {
a.forEach((c) => c.dispose());
});
this.cellMeshes = [];
this.bitWorld.clear();
}

random() {
this.clearCell();
this.bitWorld.random();
}

setRLE(rle: string) {
setRLE(this.bitWorld, rle);
}

setCellColor(colorHex: string) {
this.cellMaterial.diffuseColor = BABYLON.Color3.FromHexString(colorHex);
}
}
36 changes: 21 additions & 15 deletions src/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,39 @@ export const $canvas = document.querySelector(
"#renderCanvas"
) as HTMLCanvasElement;

export const $autoRandom = document.querySelector(
"#auto-random"
) as HTMLInputElement;
export const $fullScreen = document.querySelector(
"#full-screen"
export const $configButton = document.querySelector(
"#configButton"
) as HTMLElement;
export const $colorInput = document.querySelector("#color") as HTMLInputElement;

// dialog
export const $settingsDialog = document.querySelector(
"#settingsDialog"
) as HTMLDialogElement;
export const $historySizeInput = document.querySelector(
"#historySize"
) as HTMLInputElement;

export const $closeSettings = document.querySelector(
"#closeSettings"
) as HTMLElement;

export const $configButton = document.querySelector(
"#configButton"
) as HTMLElement;
export const $historySizeInput = document.querySelector(
"#historySize"
) as HTMLInputElement;
export const $colorInput = document.querySelector("#color") as HTMLInputElement;

export const $readRLE = document.querySelector("#readRLE") as HTMLButtonElement;
export const $rleErrorMessage = document.querySelector(
"#rleError"
export const $autoRandom = document.querySelector(
"#auto-random"
) as HTMLInputElement;

export const $autoRotate = document.querySelector(
"#auto-rotate"
) as HTMLInputElement;
export const $fullScreen = document.querySelector(
"#full-screen"
) as HTMLElement;

export const $inputRLE = document.querySelector(
"#inputRLE"
) as HTMLTextAreaElement;
export const $rleErrorMessage = document.querySelector(
"#rleError"
) as HTMLElement;
export const $readRLE = document.querySelector("#readRLE") as HTMLButtonElement;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
112 changes: 19 additions & 93 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import "./style.css";
import * as BABYLON from "babylonjs";
import { Engine, Vector3 } from "babylonjs";
import { BitGrid, BitWorld } from "@ca-ts/algo/bit";
import { setupArcRotateCamera } from "./camera";
import { createTemplateCell } from "./cell";
import { setRLE } from "./setRLE";
import { setupFullScreenButton } from "./settings";
import { setupArcRotateCamera } from "./lib/camera";
import { setupFullScreenButton } from "./lib/settings";
import {
$autoRandom,
$autoRotate,
$canvas,
$closeSettings,
$colorInput,
Expand All @@ -19,9 +17,7 @@ import {
$rleErrorMessage,
$settingsDialog,
} from "./bind";

const WORLD_SIZE = 32 * 2;
let historySize = 16;
import { App } from "./app";

const engine = new Engine($canvas, true);
const scene = new BABYLON.Scene(engine);
Expand All @@ -32,12 +28,6 @@ scene.clearColor = new BABYLON.Color4(0, 0, 0, 1);
// ArcRotateCameraをBitWorldの中心に設定
const camera = setupArcRotateCamera(scene, $canvas);

let prevGrid: BitGrid | null = null;
let prevPrevGrid: BitGrid | null = null;

// 基本的なライトを追加
new BABYLON.HemisphericLight("light1", new Vector3(0, 1, 0), scene);

const pointLight = new BABYLON.PointLight(
"pointLight",
new BABYLON.Vector3(-100, 100, 0), // ライトの初期位置
Expand All @@ -47,72 +37,16 @@ pointLight.intensity = 0.3; // 光の強さ
pointLight.diffuse = new BABYLON.Color3(0.8, 1, 1); // 光の色
pointLight.specular = new BABYLON.Color3(1, 1, 1); // 反射光の色

// BitWorldを作成
const bitWorld = BitWorld.make({ width: WORLD_SIZE, height: WORLD_SIZE });
bitWorld.random();

// BitWorldの中心を計算
const worldCenterX = bitWorld.getWidth() / 2;
const worldCenterY = bitWorld.getHeight() / 2;
camera.target = new Vector3(worldCenterX, 0, worldCenterY);

let generation = 0;
let cellMeshes: BABYLON.InstancedMesh[][] = [];
const stackHeight = 1;

const { templateCell, cellMaterial } = createTemplateCell(scene);

function updateWorld() {
prevPrevGrid = prevGrid;
prevGrid = bitWorld.bitGrid.clone();
bitWorld.next();

if (
$autoRandom.checked &&
prevPrevGrid &&
bitWorld.bitGrid.equal(prevPrevGrid)
) {
clearCell();
bitWorld.random();
}
const app = new App(engine, scene, camera, pointLight);

// 基本的なライトを追加
new BABYLON.HemisphericLight("light1", new Vector3(0, 1, 0), scene);

const newCells: BABYLON.InstancedMesh[] = [];

// 新しい世代のセルを表示
bitWorld.forEach((x, y, alive) => {
if (alive === 1) {
// セルのインスタンスを作成
const instance = templateCell.createInstance(`cell_${x}_${y}`);
instance.position = new Vector3(x, generation * stackHeight, y);

// マテリアルや色はテンプレートセルと共有されるので追加設定不要
newCells.push(instance);
// instance.scaling = new Vector3(0.5, 0.5, 0.5);
}
});

cellMeshes.push(newCells);

// 古い世代のセルを削除
if (cellMeshes.length >= historySize) {
const old = cellMeshes.slice(0, cellMeshes.length - historySize);
old.forEach((a) => {
a.forEach((c) => c.dispose());
});
cellMeshes = cellMeshes.slice(cellMeshes.length - historySize);
}
// 点光源の位置を移動させる
pointLight.position.y += stackHeight;
// カメラを移動
camera.target.y += stackHeight;
generation++;
}
const autoRotate = document.querySelector("#auto-rotate") as HTMLInputElement;
let running = true;
let i = 0;

engine.runRenderLoop(() => {
if (autoRotate.checked) {
if ($autoRotate.checked) {
camera.alpha += scene.deltaTime / 4000;
}
scene.render();
Expand All @@ -123,22 +57,14 @@ engine.runRenderLoop(() => {

i++;
if (i % INTERVAL === 0) {
updateWorld();
app.updateWorld();
}
});

window.addEventListener("resize", () => {
engine.resize();
});

function clearCell() {
cellMeshes.forEach((a) => {
a.forEach((c) => c.dispose());
});
cellMeshes = [];
bitWorld.clear();
}

document.addEventListener("keydown", (e) => {
if (e.isComposing) {
return;
Expand All @@ -147,20 +73,19 @@ document.addEventListener("keydown", (e) => {
running = !running;
}
if (e.key === "r") {
clearCell();
bitWorld.random();
app.random();
}
});

// ダイアログ制御
$historySizeInput.addEventListener("input", () => {
historySize = parseInt($historySizeInput.value, 10);
if (Number.isNaN(historySize)) {
let historySize = parseInt($historySizeInput.value, 10);
if (Number.isNaN(app.historySize)) {
historySize = 1;
}
historySize = Math.min(Math.max(historySize, 1), 500);
app.historySize = Math.min(Math.max(historySize, 1), 500);
});
$historySizeInput.value = historySize.toString();
$historySizeInput.value = app.historySize.toString();

$closeSettings.addEventListener("click", () => {
$settingsDialog.close();
Expand All @@ -186,19 +111,20 @@ $inputRLE.addEventListener("input", () => {

$readRLE.addEventListener("click", () => {
$autoRandom.checked = false;
clearCell();
app.clearCell();
$rleErrorMessage.textContent = null;
try {
setRLE(bitWorld, $inputRLE.value);
app.setRLE($inputRLE.value);
} catch (error) {
$rleErrorMessage.textContent = "Invalid RLE or oversized";
app.clearCell();
throw error;
}
$settingsDialog.close();
});

$colorInput.addEventListener("input", () => {
cellMaterial.diffuseColor = BABYLON.Color3.FromHexString($colorInput.value);
app.setCellColor($colorInput.value);
});

setupFullScreenButton($fullScreen, () => {
Expand Down

0 comments on commit 6148436

Please sign in to comment.