Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add heat map #8

Merged
merged 1 commit into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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";
}