mirror of
https://github.com/codedownio/time-ghc-modules.git
synced 2024-10-03 16:48:27 +03:00
Progress on treemap
This commit is contained in:
parent
a6319c05e2
commit
34e9ddccaf
810
package-lock.json
generated
810
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -20,20 +20,30 @@
|
||||
},
|
||||
"homepage": "https://github.com/codedownio/time-ghc-modules#readme",
|
||||
"devDependencies": {
|
||||
"@types/d3-hierarchy": "^3.1.6",
|
||||
"@types/d3-interpolate": "^3.0.4",
|
||||
"@types/d3-scale": "^4.0.8",
|
||||
"@types/d3-scale-chromatic": "^3.0.3",
|
||||
"@types/d3-selection": "^3.0.10",
|
||||
"@types/lodash": "^4.14.171",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"process": "^0.11.10",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.12.3",
|
||||
"apexcharts": "^3.27.3",
|
||||
"d3-hierarchy": "^3.1.2",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-scale-chromatic": "^3.0.0",
|
||||
"d3-selection": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"parcel": "^2.11.0",
|
||||
"react": "^17.0.2",
|
||||
"react-apexcharts": "^1.3.9",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-vis": "^1.12.1",
|
||||
"tachyons": "^4.12.0",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
|
155
src/D3TreeMap.tsx
Normal file
155
src/D3TreeMap.tsx
Normal file
@ -0,0 +1,155 @@
|
||||
import { type TreemapLayout, hierarchy, treemap, treemapBinary } from "d3-hierarchy";
|
||||
import { interpolateRgb } from "d3-interpolate";
|
||||
import { scaleOrdinal } from "d3-scale";
|
||||
import { schemeCategory10 } from "d3-scale-chromatic";
|
||||
import { select } from "d3-selection";
|
||||
import { isEqual, keys } from "lodash";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
|
||||
interface ITreeMapProps<D> {
|
||||
data: Tree<D>;
|
||||
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
labelFn: (d: D) => string;
|
||||
subLabelFn: (d: D) => string;
|
||||
valueFn: (d: D) => number;
|
||||
}
|
||||
|
||||
interface ITreeMapState<D> {
|
||||
cell: any;
|
||||
treemap: TreemapLayout<D>;
|
||||
}
|
||||
|
||||
export default class TreeMap<D> extends PureComponent<ITreeMapProps<D>, ITreeMapState<D>> {
|
||||
svg: SVGSVGElement;
|
||||
|
||||
constructor(props: ITreeMapProps<D>) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
cell: null,
|
||||
treemap: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const width = this.props.width;
|
||||
const height = this.props.height;
|
||||
|
||||
select(this.svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${Math.min(width, height)} ${Math.min(width, height)}`)
|
||||
.attr("preserveAspectRatio", "xMinYMin");
|
||||
|
||||
const tm = treemap<D>()
|
||||
.tile(treemapBinary) // treemapSquarify, treemapSliceDice, treemapSlice, treemapDice
|
||||
.size([width, height])
|
||||
.round(true)
|
||||
.paddingInner(1);
|
||||
|
||||
this.setState({ ...this.state, treemap: tm });
|
||||
|
||||
this.refresh(this.svg, tm);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ITreeMapProps<D>, _prevState: ITreeMapState<D>) {
|
||||
if (prevProps.data !== this.props.data) {
|
||||
this.refresh(this.svg, this.state.treemap, isEqual(keys(prevProps.data), keys(this.props.data)));
|
||||
}
|
||||
}
|
||||
|
||||
refresh(svg: SVGSVGElement, treemap: TreemapLayout<D>, sameNodes = true) {
|
||||
if (!this.state.cell || !sameNodes) {
|
||||
select(svg).selectAll("*").remove();
|
||||
const cell = this.drawData(svg, treemap, this.props.data);
|
||||
this.setState({ ...this.state, cell });
|
||||
} else {
|
||||
const root = hierarchy(this.props.data)
|
||||
.eachBefore((d) => (d.data.id = (d.parent ? d.parent.name + "." : "") + d.name))
|
||||
.sum((d) => this.props.valueFn(d))
|
||||
.sort((a, b) => b.height - a.height || b.value - a.value);
|
||||
|
||||
treemap(root);
|
||||
|
||||
this.state.cell.data(root.leaves());
|
||||
|
||||
this.state.cell
|
||||
.transition()
|
||||
.duration(750)
|
||||
.attr("transform", (d) => "translate(" + d.x0 + "," + d.y0 + ")")
|
||||
.select("rect")
|
||||
.attr("width", (d) => d.x1 - d.x0)
|
||||
.attr("height", (d) => d.y1 - d.y0);
|
||||
}
|
||||
}
|
||||
|
||||
drawData(svg: SVGSVGElement, treemap, data) {
|
||||
const fader = (color) => interpolateRgb(color, "#fff")(0.2);
|
||||
const color = scaleOrdinal(schemeCategory10.map(fader));
|
||||
|
||||
const root = hierarchy(data)
|
||||
.eachBefore((d) => (d.data.id = (d.parent ? d.parent.data.id + "." : "") + d.data.name))
|
||||
.sum((d) => this.props.valueFn(d))
|
||||
.sort((a, b) => b.height - a.height || b.value - a.value);
|
||||
|
||||
treemap(root);
|
||||
|
||||
console.log("root", root);
|
||||
|
||||
const cell = select(svg)
|
||||
.selectAll("g")
|
||||
.data(root.leaves())
|
||||
.enter()
|
||||
.append("g")
|
||||
.attr("transform", (d: any) => "translate(" + d.x0 + "," + d.y0 + ")");
|
||||
|
||||
// Add labels
|
||||
cell
|
||||
.append("rect")
|
||||
.attr("id", (d) => d.data.id)
|
||||
.attr("width", (d: any) => d.x1 - d.x0)
|
||||
.attr("height", (d: any) => d.y1 - d.y0)
|
||||
.attr("fill", (d) => color(this.props.labelFn(d.data)));
|
||||
|
||||
// Add clip paths to ensure text doesn't overflow
|
||||
cell
|
||||
.append("clipPath")
|
||||
.attr("id", (d) => "clip-" + d.data.id)
|
||||
.append("use")
|
||||
.attr("xlink:href", (d) => "#" + d.data.id);
|
||||
|
||||
// Append multiline text. The last line shows the value and has a specific formatting.
|
||||
cell
|
||||
.append("text")
|
||||
.attr("clip-path", (d) => "url(#clip-" + d.data.id + ")")
|
||||
.filter((d) => {
|
||||
console.log("d: ", d);
|
||||
return true;
|
||||
})
|
||||
.selectAll("tspan")
|
||||
.data((d) => [this.props.labelFn(d.data) + " | " + this.props.subLabelFn(d.data)])
|
||||
.enter()
|
||||
.append("tspan")
|
||||
.attr("x", 4)
|
||||
.attr("y", (d, i) => 10 + i * 10)
|
||||
.text((d: string) => d)
|
||||
.style("font-size", "8px");
|
||||
|
||||
cell.append("title")
|
||||
.text((d) => this.props.labelFn(d.data) + ` (${this.props.subLabelFn(d.data)})`);
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="tree-map">
|
||||
<svg ref={(c) => (this.svg = c)}></svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
|
||||
import {size} from "lodash";
|
||||
import {forEach, size} from "lodash";
|
||||
import {useMemo} from "react";
|
||||
import {Treemap as RVTreeMap} from "react-vis";
|
||||
|
||||
import * as styles from "react-vis/dist/style.css";
|
||||
import D3TreeMap from "./D3TreeMap";
|
||||
|
||||
// import {formatBytes, formatTime} from "./Util";
|
||||
|
||||
@ -30,8 +29,9 @@ export default function TreeMap({aggregate, data}: Props) {
|
||||
let current = ret;
|
||||
let soFar = "";
|
||||
const parts = module.module.split(".");
|
||||
for (const part of parts) {
|
||||
soFar += "." + part;
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
const part = parts[i];
|
||||
soFar += (i === 0 ? part : "." + part);
|
||||
current.children[part] = current.children[part] || {
|
||||
title: soFar,
|
||||
value: 0,
|
||||
@ -41,53 +41,70 @@ export default function TreeMap({aggregate, data}: Props) {
|
||||
current.value += (aggregate === "time" ? module["time"]: module["alloc"]);
|
||||
}
|
||||
}
|
||||
forEach(ret.children, (x) => {
|
||||
ret.value += x.value;
|
||||
});
|
||||
|
||||
console.log("Built nestedData", ret);
|
||||
|
||||
return ret;
|
||||
}, [modulesList]);
|
||||
|
||||
const myData = {
|
||||
"title": "analytics",
|
||||
const myData: Tree<TreeNode> = {
|
||||
"name": "analytics",
|
||||
"color": "#12939A",
|
||||
"size": 48716,
|
||||
"children": [
|
||||
{
|
||||
"title": "cluster",
|
||||
"name": "cluster",
|
||||
"color": "#12939A",
|
||||
"size": 15207,
|
||||
"children": [
|
||||
{"title": "AgglomerativeCluster", "color": "#12939A", "size": 3938},
|
||||
{"title": "CommunityStructure", "color": "#12939A", "size": 3812},
|
||||
{"title": "HierarchicalCluster", "color": "#12939A", "size": 6714},
|
||||
{"title": "MergeEdge", "color": "#12939A", "size": 743}
|
||||
{"name": "AgglomerativeCluster", "color": "#12939A", "size": 3938},
|
||||
{"name": "CommunityStructure", "color": "#12939A", "size": 3812},
|
||||
{"name": "HierarchicalCluster", "color": "#12939A", "size": 6714},
|
||||
{"name": "MergeEdge", "color": "#12939A", "size": 743}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "graph",
|
||||
"name": "graph",
|
||||
"color": "#12939A",
|
||||
"size": 26435,
|
||||
"children": [
|
||||
{"title": "BetweennessCentrality", "color": "#12939A", "size": 3534},
|
||||
{"title": "LinkDistance", "color": "#12939A", "size": 5731},
|
||||
{"title": "MaxFlowMinCut", "color": "#12939A", "size": 7840},
|
||||
{"title": "ShortestPaths", "color": "#12939A", "size": 5914},
|
||||
{"title": "SpanningTree", "color": "#12939A", "size": 3416}
|
||||
{"name": "BetweennessCentrality", "color": "#12939A", "size": 3534},
|
||||
{"name": "LinkDistance", "color": "#12939A", "size": 5731},
|
||||
{"name": "MaxFlowMinCut", "color": "#12939A", "size": 7840},
|
||||
{"name": "ShortestPaths", "color": "#12939A", "size": 5914},
|
||||
{"name": "SpanningTree", "color": "#12939A", "size": 3416}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "optimization",
|
||||
"name": "optimization",
|
||||
"color": "#12939A",
|
||||
"size": 7074,
|
||||
"children": [
|
||||
{"title": "AspectRatioBanker", "color": "#12939A", "size": 7074}
|
||||
{"name": "AspectRatioBanker", "color": "#12939A", "size": 7074}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Modes: squarify, resquarify, slice, dice, slicedice, binary, circlePack, partition, partition-pivot
|
||||
|
||||
return (
|
||||
<RVTreeMap
|
||||
title={'My New Treemap'}
|
||||
<D3TreeMap
|
||||
width={500}
|
||||
height={500}
|
||||
data={myData}
|
||||
mode="squarify"
|
||||
labelFn={(x) => {
|
||||
return x.name;
|
||||
}}
|
||||
subLabelFn={(x) => {
|
||||
return x.size + " UNITS";
|
||||
}}
|
||||
valueFn={(x) => {
|
||||
return x.size;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
11
src/types.d.ts
vendored
11
src/types.d.ts
vendored
@ -17,3 +17,14 @@ interface ModuleData {
|
||||
modules: Array<{module: string;}>;
|
||||
data: Array<Event>;
|
||||
}
|
||||
|
||||
type Tree<D> = D & {
|
||||
children?: Tree<D>[];
|
||||
id?: string;
|
||||
}
|
||||
|
||||
interface TreeNode {
|
||||
name: string;
|
||||
color: string;
|
||||
size: number;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"moduleResolution": "node",
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
|
Loading…
Reference in New Issue
Block a user