Skip to content

Commit

Permalink
chore: stbale
Browse files Browse the repository at this point in the history
  • Loading branch information
nonzzz committed May 26, 2024
1 parent 3610a7e commit 9f7e336
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 29 deletions.
12 changes: 6 additions & 6 deletions src/client/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Sidebar, SidebarProvider } from './components/side-bar'
import { Treemap } from './components/treemap'
import type { TreemapInstance } from './components/treemap'

// import { ModuleSize, TreeMap } from './components/tree-map'
import { ModuleSize, TreeMap } from './components/tree-map'

export function App() {
const treeMapRef = useRef<TreemapInstance>()
Expand Down Expand Up @@ -51,11 +51,11 @@ export function App() {
{/* <ModuleSize module={tooltipContent} sizes="statSize" checkedSizes={sizes} /> */}
{/* <ModuleSize module={tooltipContent} sizes="parsedSize" checkedSizes={sizes} /> */}
{/* <ModuleSize module={tooltipContent} sizes="gzipSize" checkedSizes={sizes} /> */}
<Text p font="12px">
path:
{' '}
{tooltipContent.filename}
</Text>
{
/* <Text p font="12px">
path: {tooltipContent.filename}
</Text> */
}
</>
)}
</Tooltip>
Expand Down
5 changes: 2 additions & 3 deletions src/client/components/tree-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ export function ModuleSize(props: { module: FoamDataObject; sizes: Sizes; checke
if (!module[sizes]) return null
return (
<Text p font="12px">
{checkedSizes === sizes ? <Text span b font="12px">{sizes}</Text> : <Text span>{sizes}</Text>}
{' '}
:
{checkedSizes === sizes ? <Text span b font="12px">{sizes}</Text> : <Text span>{sizes}</Text>} :
<Text b font="12px">{convertBytes(module[sizes])}</Text>
</Text>
)
Expand Down Expand Up @@ -177,6 +175,7 @@ export const TreeMap = forwardRef<TreeMapComponent, TreeMapProps>(function TreeM
const root = findGroupRoot(properties.group, foamTreeInstance.current)
const chunkName = getChunkNamePart(root.label, chunkNamePartIndex)
const hash = /[^0-9]/.test(chunkName) ? hashCode(chunkName) : (parseInt(chunkName) / 1000) * 360
console.log(Math.round(Math.abs(hash) % 360), 'hsla(317deg, 60%, 50%, 0.9)')
variables.groupColor = {
model: 'hsla',
h: Math.round(Math.abs(hash) % 360),
Expand Down
3 changes: 1 addition & 2 deletions src/client/components/treemap/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ interface TreemapProps {

export type TreemapInstance = ReturnType<typeof createTreemap>

// We need sort the chunks by byte. We can't sort the chunks at backend side, because we can't determine the order.

export const Treemap = forwardRef((props: TreemapProps, ref: ForwardedRef<TreemapInstance>) => {
const treemapInstance = useRef<TreemapInstance | null>(null)
const containerRef = useRef<HTMLDivElement>(null)
Expand All @@ -41,6 +39,7 @@ export const Treemap = forwardRef((props: TreemapProps, ref: ForwardedRef<Treema
}

useEffect(() => {
if (!visibleChunks.length) return
if (!treemapInstance.current && containerRef.current) {
const treemap = createTreemap(visibleChunks)
treemapInstance.current = treemap
Expand Down
7 changes: 7 additions & 0 deletions src/client/components/treemap/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,11 @@ import { Module as _Module } from '../../interface'
export interface Module extends _Module {
layout?: [number, number, number, number]
[prop: string]: any
groups: Module[]
}

export interface SquarifiedModule {
node: Omit<Module, 'groups'>
layout: [number, number, number, number]
children: SquarifiedModule[]
}
2 changes: 1 addition & 1 deletion src/client/components/treemap/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export function sortChildrenBySize(a: Module, b: Module) {
return b.size - a.size || +(a.id > b.id) - +(a.id < b.id)
}

export function assignColorMaapings(hueAngle: number) {
export function hueAngleToColor(hueAngle: number) {
const saturation = 0.6 + 0.4 * Math.max(0, Math.cos(hueAngle))
const lightness = 0.5 + 0.2 * Math.max(0, Math.cos(hueAngle + Math.PI * 2 / 3))
return 'hsl(' + hueAngle * 180 / Math.PI + 'deg, ' + Math.round(100 * saturation) + '%, ' + Math.round(100 * lightness) + '%)'
Expand Down
72 changes: 72 additions & 0 deletions src/client/components/treemap/squared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { Module, SquarifiedModule } from './interface'

function recursion<T extends Module>(data: T[], x: number, y: number, w: number, h: number): SquarifiedModule[] {
const squarifiedData: SquarifiedModule[] = []
if (!data) return squarifiedData
const worst = (start: number, end: number, shortSide: number, totalSizes: number, sizeToArea: number) => {
const first = data[start]
const last = data[end]
const maxArea = first.size * sizeToArea
const minArea = last.size * sizeToArea
return Math.max(
((shortSide ** 2) * maxArea) / (totalSizes ** 2),
(totalSizes * totalSizes) / ((shortSide ** 2) * minArea)
)
}

const squarify = (start: number, x: number, y: number, w: number, h: number) => {
while (start < data.length) {
let totalSizes = 0
for (let i = start; i < data.length; i++) {
totalSizes += data[i].size
}
const shortSide = Math.min(w, h)
const sizeToArea = (w * h) / totalSizes
let end = start
let areaInRun = 0
let oldWorst = 0

while (end < data.length) {
const area = data[end].size * sizeToArea
const newWorst = worst(start, end, shortSide, areaInRun + area, sizeToArea)
if (end > start && oldWorst < newWorst) break
areaInRun += area
oldWorst = newWorst
end++
}

const split = Math.round(areaInRun / shortSide)
let areaInLayout = 0
for (let i = start; i < end; i++) {
const node = data[i]
const area = node.size * sizeToArea
const lower = Math.round(shortSide * areaInLayout / areaInRun)
const upper = Math.round(shortSide * (areaInLayout + area) / areaInRun)
const [cx, cy, cw, ch] = w >= h
? [x, y + lower, split, upper - lower]
: [x + lower, y, upper - lower, split]
const { groups, ...rest } = node
squarifiedData.push({
node: rest,
layout: [cx + 4, cy + 20, cw - 8, ch - 24],
children: cw > 8 && ch > 24
? recursion(groups, cx, cy, cw, ch)
: []
})
areaInLayout += area
}
start = end
if (w >= h) {
x += split
w -= split
} else {
y += split
h -= split
}
}
}
squarify(0, x, y, w, h)
return squarifiedData
}

export { recursion as squarify }
72 changes: 55 additions & 17 deletions src/client/components/treemap/treemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
// Thanks Squarified Treemap by Mark Bruls, Kees Huizing, and Jarke J. van Wijk
// https://www.win.tue.nl/~vanwijk/stm.pdf

import type { Module } from './interface'
import type { Module, SquarifiedModule } from './interface'
import { assignColorMaapings, hueAngleToColor } from './shared'
import { squarify } from './squared'

interface Shape {
width: number
Expand All @@ -16,13 +18,15 @@ interface Shape {

const defaultShape = { width: 0, height: 0 }

type ColorMapping = Record<string, ColorMapping>

export class Paint {
private canvas: HTMLCanvasElement
private context: CanvasRenderingContext2D
private shape: Shape
private mainEl: HTMLElement | null
private data: Module[]
private layoutNodes: Module[]
private layoutNodes: SquarifiedModule[]
private colorMapping: Record<string, string>
constructor(data: Module[]) {
this.mainEl = null
Expand All @@ -31,33 +35,68 @@ export class Paint {
this.canvas = document.createElement('canvas')
this.context = this.canvas.getContext('2d')!
this.shape = { ...defaultShape }
this.colorMapping = {}
}

private drawNodeBackground(node: SquarifiedModule) {
//
const [x, y, w, h] = node.layout
for (const child of node.children) {
this.drawNodeBackground(child)
}
if (node.children.length) {
//
this.context.fillStyle = 'hsl(180deg, 100%, 50%)'
this.context.fillRect(x, y, w, 20)
this.context.fillRect(x, y + h - 4, w, 4)
this.context.fillRect(x, y + 20, 4, h - 24)
this.context.fillRect(x + w - 4, y + 20, 4, h - 24)
} else {
this.context.fillStyle = this.colorMapping[node.node.filename]
this.context.fillRect(x, y, w, h)
// console.log(this.colorMapping[node.node.filename], node.node.filename)
}
}

private draw() {
// cleanup layout
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
console.log(this.layoutNodes)
for (const node of this.layoutNodes) {
this.drawNodeBackground(node)
// this.context.fillStyle = this.colorMapping[node.node.filename]
// this.context.fillRect(x, y, w, h)
}
}

// pre handle the data and setup the color mapping
private updateColorMapping() {
const root: Record<string, string> = {}
const colorMapping: Record<string, string> = {}
const accumulatePath = (node: Module, path: string = '') => {
path += node.id + '/'
root[path.substring(0, path.length - 1)] = ''
if (!node.groups) return
// console.log(node.groups.sort(sortChildrenBySize))
for (const group of node.groups) {
accumulatePath(group, path)
const defaultSweepAngle = Math.PI * 2

const totalSize = this.data.reduce((acc, module) => acc + module.size, 0)

const assignColorByDirectory = (module: Module, parent: Module, startAngle: number, sweepAngle: number) => {
const childSweepAngle = module.size / parent.size * sweepAngle
colorMapping[module.filename] = hueAngleToColor(startAngle + childSweepAngle / 2)
if (module.groups) {
for (const child of module.groups) {
const childSweepAngle = child.size / module.size * sweepAngle
assignColorByDirectory(child, module, startAngle, childSweepAngle)
startAngle += childSweepAngle
}
}
}

for (const arcana of this.data) {
for (const group of arcana.groups) {
accumulatePath(group)
for (const module of this.data) {
const initalAngle = module.size / totalSize * defaultSweepAngle
colorMapping[module.filename] = hueAngleToColor(initalAngle)
for (const group of module.groups) {
assignColorByDirectory(group, module, initalAngle, defaultSweepAngle)
}
}
// assign color
console.log(this.data)

this.colorMapping = colorMapping
}

dispose() {
Expand All @@ -83,8 +122,7 @@ export class Paint {
this.canvas.style.cssText = `width: ${w}px; height: ${h}px`
this.context.scale(ratio, ratio)
if (w !== prviousShape.width || h !== prviousShape.height) {
// this.layoutNodes = squarify(this.data, 0, 0, this.shape.width - 1, this.shape.height - 1)
// squarify(this.data, 0, 0, this.shape.width - 1, this.shape.height - 1)
this.layoutNodes = squarify(this.data, 0, 0, this.shape.width, this.shape.height)
}
this.draw()
}
Expand Down

0 comments on commit 9f7e336

Please sign in to comment.