Skip to content

Commit

Permalink
implement the cluster in time transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
molti-tasking committed Nov 24, 2024
1 parent 918eda7 commit 8c2cc01
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 42 deletions.
88 changes: 70 additions & 18 deletions src/components/AggregatedClusterView.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
import { ClusterView } from "@/lib/clusteringOverTime";
import { cn } from "@/lib/utils";
import { useRawDataStore } from "@/store/useRawDataStore";
import { useViewModelStore } from "@/store/useViewModelStore";
import { useViewSettingsStore } from "@/store/useViewSettingsStore";
import {
utcDay,
utcFormat,
utcHour,
utcMinute,
utcMonth,
utcSecond,
utcWeek,
utcYear,
} from "d3";
import { useEffect } from "react";
import { clusterColors } from "./clusterColors";

export const AggregatedClusterView = () => {
// const values = useRawDataStore((state) => state.values);
// // const dimensions = useRawDataStore((state) => state.dimensions);
const values = useRawDataStore((state) => state.values);

// const presentationSettings = useViewSettingsStore();
const presentationSettings = useViewSettingsStore();

// const { colsAccordingToAggregation, processData } = useViewModelStore();
const { clustersInTime, processClustersInTimeData } = useViewModelStore();

// useEffect(() => {
// processData();
// }, [presentationSettings, values]);

const { colsAccordingToAggregation } = useViewModelStore();

const clustersInTime: ClusterView[] = [
{ timestamp: "asdf", clusters: colsAccordingToAggregation },
{ timestamp: "asdf", clusters: colsAccordingToAggregation },
{ timestamp: "asdf", clusters: colsAccordingToAggregation },
useEffect(() => {
processClustersInTimeData();
}, [presentationSettings, values]);
const interestingTimestampIndizes = [
0,
Math.floor(clustersInTime.length / 4),
Math.floor(clustersInTime.length / 2),
Math.floor((clustersInTime.length * 3) / 4),
clustersInTime.length - 1,
];

return (
<div className="flex flex-1 flex-row w-full my-2">
<div className="flex flex-1 flex-row w-full my-2 pb-8 relative">
{clustersInTime.map(({ timestamp, clusters }, groupIndex) => (
<div
key={`${groupIndex}-${timestamp}`}
className={cn(
"flex flex-1 flex-col flex-shrink items-center overflow-hidden",
"flex flex-1 flex-col flex-shrink items-center overflow-hidden ",
groupIndex > 0 ? "border-l-[0.5px] border-white" : "rounded-l-sm"
)}
>
Expand All @@ -41,9 +53,49 @@ export const AggregatedClusterView = () => {
)}
></div>
))}
<div>{timestamp}</div>

{!!interestingTimestampIndizes.includes(groupIndex) && (
<div
className={cn(
"bg-gray-200 overflow-visible absolute",
"bottom-0",
"h-8 p-2 rounded-sm text-xs"
)}
>
{multiFormat(new Date(Number(timestamp)))}
</div>
)}
</div>
))}
</div>
);
};

const formatMillisecond = utcFormat(".%L"),
formatSecond = utcFormat(":%S"),
formatMinute = utcFormat("%I:%M"),
formatHour = utcFormat("%I %p"),
formatDay = utcFormat("%a %d"),
formatWeek = utcFormat("%b %d"),
formatMonth = utcFormat("%B"),
formatYear = utcFormat("%Y");

function multiFormat(date: Date) {
return (
utcSecond(date) < date
? formatMillisecond
: utcMinute(date) < date
? formatSecond
: utcHour(date) < date
? formatMinute
: utcDay(date) < date
? formatHour
: utcMonth(date) < date
? utcWeek(date) < date
? formatDay
: formatWeek
: utcYear(date) < date
? formatMonth
: formatYear
)(date);
}
2 changes: 2 additions & 0 deletions src/lib/clustering.worker.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as Comlink from "comlink";
import { aggregator } from "./clustering";
import { clusteringOverTime } from "./clusteringOverTime";

// Define the functions that will be available in the worker
const workerFunctions = {
aggregator: aggregator,
clusteringOverTime: clusteringOverTime,
};

export type ClusteringWorker = typeof workerFunctions;
Expand Down
5 changes: 4 additions & 1 deletion src/lib/clusteringOverTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export const clusteringOverTime = (
// ----------------
// Clustering context window: The amount of data entries that should be taken into consideration for handling the clusters later on.
// ----------------
const clusteringContextWindow = settings.dataTicks ?? 20; // 20 just as a dump fallback value
const clusteringContextWindow = !!settings.dataTicks
? settings.dataTicks
: 20; // 20 just as a dump fallback value

for (
let dataEntryIndex = 0;
Expand All @@ -52,6 +54,7 @@ export const clusteringOverTime = (
: "";

const aggregated = clusteringData(dataToBeClustered, dimensions, settings);

const clusters: [string, number][] = dimensions.map((val) => [
val,
aggregated.findIndex((entries) => Object.keys(entries[0]).includes(val)),
Expand Down
6 changes: 0 additions & 6 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import ErrorPage from "./error-page";
import "./index.css";
import AggregatedClusters from "./routes/aggregated-clusters";
import AggrLine from "./routes/aggregated-line";
import Anu from "./routes/anu";
import Home from "./routes/home";
Expand Down Expand Up @@ -35,11 +34,6 @@ const router = createBrowserRouter([
element: <MutliAggregatedLine />,
errorElement: <ErrorPage />,
},
{
path: "aggregated-clusters",
element: <AggregatedClusters />,
errorElement: <ErrorPage />,
},
],
},
]);
Expand Down
5 changes: 0 additions & 5 deletions src/routes/aggregated-clusters.tsx

This file was deleted.

3 changes: 1 addition & 2 deletions src/routes/multi-aggregated-line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { useViewSettingsStore } from "@/store/useViewSettingsStore";

export default function MutliAggregatedLine() {
const mode = useViewSettingsStore((state) => state.mode);

const { aggregated } = useViewModelStore();
const aggregated = useViewModelStore((data) => data.aggregated);

return (
<div className="container w-full my-2 flex flex-col flex-wrap gap-2">
Expand Down
1 change: 0 additions & 1 deletion src/routes/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default function Root() {
{ title: "Multi Line", href: "multi-line" },
{ title: "Aggregation", href: "aggregated-line" },
{ title: "Multi Aggregation", href: "multi-aggregated-line" },
{ title: "Cluster Aggregation", href: "aggregated-clusters" },
];

return (
Expand Down
46 changes: 39 additions & 7 deletions src/store/useViewModelStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@ import { create } from "zustand";
import { useRawDataStore } from "./useRawDataStore";
import { useViewSettingsStore } from "./useViewSettingsStore";

import { ClusterView } from "@/lib/clusteringOverTime";
import Worker from "../lib/clustering.worker?worker";

interface DataStore {
aggregated: Record<string, number>[][];
yDomain: [number, number];
colsAccordingToAggregation: [string, number][];

/**
* This data is needed only for certain views. It should only be calculated when needed.
*/
clustersInTime: ClusterView[];

processData: () => void;
processClustersInTimeData: () => void;
}

const workerInstance = new Worker({ name: "aggregator" });
Expand All @@ -22,10 +29,9 @@ export const useViewModelStore = create<DataStore>((set) => {
console.log("init view model store");

const throttledDataProcess = _.throttle(async () => {
console.count("Throttled process data");
const timerName = Date.now();

console.time("ViewModel process duration " + String(timerName));
console.time("ViewModel basic data process duration " + String(timerName));
const dimensions = useRawDataStore.getState().dimensions;
const values = useRawDataStore.getState().values;
const { updateSettings, ...presentationSettings } =
Expand All @@ -37,19 +43,45 @@ export const useViewModelStore = create<DataStore>((set) => {
dimensions,
presentationSettings
);
console.timeEnd("ViewModel process duration " + String(timerName));
console.timeEnd(
"ViewModel basic data process duration " + String(timerName)
);

set(aggregated);
}, 2000);

const throttledClustersInTimeProcess = _.throttle(async () => {
const timerName = Date.now();

console.time(
"ViewModel cluster in time process duration " + String(timerName)
);
const dimensions = useRawDataStore.getState().dimensions;
const values = useRawDataStore.getState().values;
const { updateSettings, ...presentationSettings } =
useViewSettingsStore.getState();
console.log("Settings: ", presentationSettings);

const { clustersInTime } = await workerApi.clusteringOverTime(
values,
dimensions,
presentationSettings
);

console.timeEnd(
"ViewModel cluster in time process duration " + String(timerName)
);

set({ clustersInTime });
}, 10000);

return {
aggregated: [],
yDomain: [0, 10],
colsAccordingToAggregation: [],
clustersInTime: [],

processData: async () => {
console.count("Process data with worker");
throttledDataProcess();
},
processData: throttledDataProcess,
processClustersInTimeData: throttledClustersInTimeProcess,
};
});
4 changes: 2 additions & 2 deletions src/store/useViewSettingsStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChartPresentationSettings } from "@/lib/ChartPresentationSettings";
import { create } from "zustand";
import { useRawDataStore } from "./useRawDataStore";
import { ChartPresentationSettings } from "@/lib/ChartPresentationSettings";

export type ViewSettingsStore = ChartPresentationSettings & {
updateSettings: (
Expand All @@ -9,7 +9,7 @@ export type ViewSettingsStore = ChartPresentationSettings & {
};

export const useViewSettingsStore = create<ViewSettingsStore>((set, get) => {
const dataTicks = useRawDataStore.getState().values.length ?? 20;
const dataTicks = Math.min(useRawDataStore.getState().values.length ?? 50);
return {
eps: 8,
dataTicks,
Expand Down

0 comments on commit 8c2cc01

Please sign in to comment.