Skip to content

Commit

Permalink
feat: add heat map (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
rei1024 authored Oct 21, 2024
1 parent 2bc1c67 commit b418880
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 25 deletions.
9 changes: 9 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ <h2>Map</h2>
/>
Frequency
</label>
<label for="map-heat-select" style="user-select: none">
<input
id="map-heat-select"
value="heat"
name="map-type-select"
type="radio"
/>
Heat
</label>
</span>
<label style="user-select: none" for="show-animation-checkbox">
<input id="show-animation-checkbox" type="checkbox" checked />
Expand Down
46 changes: 30 additions & 16 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
$showGridCheckbox,
} from "./bind";
import { setColorTable } from "./ui/colorTable";
import { displayMapTypeLower, type MapType } from "./ui/core";

const cellSize = 10;
const innerCellSize = 6;
Expand All @@ -25,18 +26,28 @@ const frequencyList = [
300, 400, 500,
];

export function dataToColor(list: number[], data: number) {
export function dataToColor(
list: number[],
data: number,
style: "hue" | "heat"
) {
const index = list.findIndex((t) => t === data) ?? 0;
const value = index / list.length;

// 80% 0.1
// return `oklch(92% 0.35 ${value * 360})`;
return `lch(70% 70 ${value * 360})`;
// return `hsl(${value * 360} 100% 70%)`;
if (style === "hue") {
const value = index / list.length;
return `lch(70% 70 ${value * 360})`;
} else if (style === "heat") {
// make red
const value = (index + 1) / list.length;
// Heat map
const h = (1 - value) * 240;
return "hsl(" + h + " 100% 70%)";
} else {
throw new Error("unknown style");
}

// Heap map
// const h = value * 240;
// return "hsl(" + h + " 100% 50%)";
// return `hsl(${value * 360} 100% 70%)`;
}

export class App {
Expand All @@ -46,7 +57,7 @@ export class App {
private gen = 0;
private valve: Valve;
private colorTableRows: HTMLTableRowElement[] = [];
private mapType: "period" | "frequency" = "period";
private mapType: MapType = "period";

constructor(private $canvas: HTMLCanvasElement) {
const ctx = this.$canvas.getContext("2d", { colorSpace: "display-p3" });
Expand Down Expand Up @@ -102,9 +113,13 @@ export class App {
const list = mapData.list;
for (const [y, row] of mapData.data.entries()) {
for (const [x, p] of row.entries()) {
if (p >= 1) {
if (p >= (this.mapType === "heat" ? 0 : 1)) {
ctx.beginPath();
ctx.fillStyle = dataToColor(list, p);
ctx.fillStyle = dataToColor(
list,
p,
this.mapType === "heat" ? "heat" : "hue"
);
ctx.rect(
(x - dx + safeArea) * cellSize,
(y - dy + safeArea) * cellSize,
Expand Down Expand Up @@ -211,6 +226,8 @@ export class App {
}
return this.mapType === "frequency"
? this.data.frequencyMap
: this.mapType === "heat"
? this.data.heatMap
: this.data.periodMap;
}

Expand Down Expand Up @@ -261,16 +278,13 @@ export class App {
this.colorTableRows[pointData.index].style.backgroundColor = "#0000FF22";

$hoverInfo.textContent =
" " +
(this.mapType === "period" ? "period" : "frequency") +
" = " +
pointData.cellData;
" " + displayMapTypeLower(this.mapType) + " = " + pointData.cellData;
} else {
$hoverInfo.textContent = " "; // 崩れないように
}
}

updateMapType(mapType: "period" | "frequency") {
updateMapType(mapType: MapType) {
this.mapType = mapType;
this.colorTableRows = setColorTable(this.getMapData(), this.mapType);
}
Expand Down
8 changes: 7 additions & 1 deletion src/lib/analyzeOscillator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ export type AnalyzeResult = {
list: number[];
countMap: Map<number, number>;
};
heatMap: {
data: number[][];
list: number[];
countMap: Map<number, number>;
};
};

export function bitGridToData(bitGrid: BitGrid): BitGridData {
Expand Down Expand Up @@ -130,7 +135,7 @@ export function analyzeOscillator(
const width = world.histories[0]?.bitGrid.getWidth() ?? 0;
const height = world.histories[0]?.bitGrid.getHeight() ?? 0;

const { periodMap, frequencyMap } = getMap({
const { periodMap, frequencyMap, heatMap } = getMap({
width,
height,
or,
Expand Down Expand Up @@ -161,5 +166,6 @@ export function analyzeOscillator(
},
periodMap,
frequencyMap,
heatMap,
};
}
37 changes: 37 additions & 0 deletions src/lib/getMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export function getMap({
list: number[];
countMap: Map<number, number>;
};
heatMap: {
data: number[][];
list: number[];
countMap: Map<number, number>;
};
} {
const periodArray = Array(height)
.fill(0)
Expand All @@ -41,17 +46,41 @@ export function getMap({
.map(() => 0)
);

const heatArray = Array(height)
.fill(0)
.map(() => 0)
.map(() =>
Array(width)
.fill(0)
.map(() => -1)
);

const historiesArray = histories.map((h) => h.getArray());

or.forEachAlive((x, y) => {
const states: (0 | 1)[] = [];
let heat = 0;
let prevCell = historiesArray[0]?.[y][x];
for (const h of historiesArray) {
const cell = h[y][x];
if (prevCell !== undefined && prevCell !== cell) {
heat++;
}
prevCell = cell;
states.push(cell);
if (cell !== 0) {
frequencyArray[y][x]++;
}
}

if (
historiesArray.length !== 0 &&
historiesArray[0][y][x] !==
historiesArray[historiesArray.length - 1][y][x]
) {
heat++;
}
heatArray[y][x] = heat;
periodArray[y][x] = findPeriod(states);
});

Expand All @@ -61,6 +90,9 @@ export function getMap({
const frequencyCountMap = getCountMap(frequencyArray);
frequencyCountMap.delete(0);

const heatCountMap = getCountMap(heatArray);
heatCountMap.delete(-1);

return {
periodMap: {
data: periodArray,
Expand All @@ -72,6 +104,11 @@ export function getMap({
list: mapUnique(frequencyArray).filter((x) => x !== 0),
countMap: frequencyCountMap,
},
heatMap: {
data: heatArray,
list: mapUnique(heatArray).filter((x) => x !== -1),
countMap: heatCountMap,
},
};
}

Expand Down
6 changes: 2 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { setDataTable } from "./ui/dataTable";

import { App } from "./app";
import { getMousePositionInElement } from "./ui/getMousePositionInElement";
import type { MapType } from "./ui/core";

const worker = new MyWorker();

Expand Down Expand Up @@ -70,17 +71,14 @@ $canvas.addEventListener("mousemove", (e) => {
});

$canvas.addEventListener("mouseleave", (e) => {
console.log(e);
const position = getMousePositionInElement($canvas, e);
app.renderColorTableHighlight(position);
});

for (const $radio of $mapTypeSelect) {
$radio.addEventListener("input", (e) => {
(e.target as HTMLInputElement).value;
app.updateMapType(
(e.target as HTMLInputElement).value as "period" | "frequency"
);
app.updateMapType((e.target as HTMLInputElement).value as MapType);
});
}

Expand Down
9 changes: 5 additions & 4 deletions src/ui/colorTable.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { dataToColor as dataToColor } from "../app";
import { dataToColor } from "../app";
import { $colorTable } from "../bind";
import { displayMapTypeTitle, type MapType } from "./core";

export function setColorTable(
map: { data: number[][]; list: number[]; countMap: Map<number, number> },
type: "period" | "frequency"
mapType: MapType
) {
const rows: HTMLTableRowElement[] = [];

Expand All @@ -19,7 +20,7 @@ export function setColorTable(
thColor.style.width = "60px";

const thType = document.createElement("th");
thType.textContent = type === "period" ? "Period" : "Frequency";
thType.textContent = displayMapTypeTitle(mapType);

const thCount = document.createElement("th");
thCount.textContent = "Count";
Expand All @@ -30,7 +31,7 @@ export function setColorTable(

for (const item of list) {
const row = document.createElement("tr");
const color = dataToColor(list, item);
const color = dataToColor(list, item, mapType === "heat" ? "heat" : "hue");
const $color = document.createElement("td");
$color.style.backgroundColor = color;
$color.style.width = "40px";
Expand Down
19 changes: 19 additions & 0 deletions src/ui/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export type MapType = "period" | "frequency" | "heat";

/**
* 表示用
*/
export function displayMapTypeLower(mapType: MapType) {
return mapType;
}

/**
* 表示用
*/
export function displayMapTypeTitle(mapType: MapType) {
return mapType === "period"
? "Period"
: mapType === "heat"
? "Heat"
: "Frequency";
}

0 comments on commit b418880

Please sign in to comment.