Skip to content

Commit

Permalink
feat: split update
Browse files Browse the repository at this point in the history
  • Loading branch information
nonzzz committed Nov 24, 2024
1 parent fd4576d commit 4755ec2
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 97 deletions.
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { nonzzz } = require('eslint-config-kagura')

module.exports = nonzzz(
{ ts: true, jsx: true, react: true, unusedImports: false },
{ ts: true, jsx: true, react: true, unusedImports: true },
{
ignores: [
'dist',
Expand Down
14 changes: 9 additions & 5 deletions src/etoile/etoile.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { asserts } from './graph'
import { Display, Graph } from './graph/display'
import { Display, S } from './graph/display'
import { Schedule } from './schedule'

function traverse(graphs: Display[], handler: (graph: Graph) => void) {
function traverse(graphs: Display[], handler: (graph: S) => void) {
const len = graphs.length
for (let i = 0; i < len; i++) {
const graph = graphs[i]
if (asserts.isBox(graph) || asserts.isLayer(graph)) {
traverse(graph.elements, handler)
} else if (asserts.isGraph(graph)) {
if (asserts.isLayer(graph) && graph.__refresh__) {
handler(graph)
continue
}
if (asserts.isGraph(graph)) {
handler(graph)
} else if (asserts.isBox(graph) || asserts.isLayer(graph)) {
traverse(graph.elements, handler)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/etoile/graph/layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { LocOptions } from './display'
export class Layer extends C implements S {
private c: Canvas
__refresh__: boolean
private options: RenderViewportOptions
options: RenderViewportOptions
width: number
height: number
x: number
Expand Down Expand Up @@ -47,9 +47,9 @@ export class Layer extends C implements S {
setCacheSnapshot(c: HTMLCanvasElement) {
const dpr = this.options.devicePixelRatio || 1
const matrix = this.matrix.create({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 })
this.ctx.clearRect(0, 0, this.options.width, this.options.height)
matrix.transform(this.x, this.y, this.scaleX, this.scaleY, this.rotation, this.skewX, this.skewY)
applyCanvasTransform(this.ctx, matrix, dpr)
this.ctx.clearRect(0, 0, this.options.width, this.options.height)
this.ctx.drawImage(c, 0, 0, this.options.width / dpr, this.options.height / dpr)
this.__refresh__ = true
}
Expand Down
66 changes: 44 additions & 22 deletions src/etoile/schedule/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,43 @@ import type { RenderViewportOptions } from './render'

export type ApplyTo = string | Element

export interface DrawGraphIntoCanvasOptions {
c: HTMLCanvasElement
ctx: CanvasRenderingContext2D
dpr: number
}

// First cleanup canvas
export function drawGraphIntoCanvas(
graph: Display,
opts: DrawGraphIntoCanvasOptions,
callback: (opt: DrawGraphIntoCanvasOptions, graph: Display) => boolean | void
) {
const { ctx, dpr } = opts
ctx.save()
if (asserts.isLayer(graph) && graph.__refresh__) {
callback(opts, graph)
return
}
if (asserts.isLayer(graph) || asserts.isBox(graph)) {
const elements = graph.elements
const cap = elements.length

for (let i = 0; i < cap; i++) {
const element = elements[i]
drawGraphIntoCanvas(element, opts, callback)
}
callback(opts, graph)
}
if (asserts.isGraph(graph)) {
const matrix = graph.matrix.create({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 })
matrix.transform(graph.x, graph.y, graph.scaleX, graph.scaleY, graph.rotation, graph.skewX, graph.skewY)
applyCanvasTransform(ctx, matrix, dpr)
graph.render(ctx)
}
ctx.restore()
}

export class Schedule extends Box {
render: Render
to: Element
Expand All @@ -33,29 +70,14 @@ export class Schedule extends Box {

// execute all graph elements
execute(render: Render, graph: Display = this) {
render.ctx.save()
if (asserts.isLayer(graph) && graph.__refresh__) {
graph.draw(render.ctx)
} else {
if (asserts.isBox(graph) || asserts.isLayer(graph)) {
const elements = graph.elements
const cap = elements.length
for (let i = 0; i < cap; i++) {
const element = elements[i]
this.execute(render, element)
}
if (asserts.isLayer(graph)) {
graph.setCacheSnapshot(render.canvas)
drawGraphIntoCanvas(graph, { c: render.canvas, ctx: render.ctx, dpr: render.options.devicePixelRatio }, (opts, graph) => {
if (asserts.isLayer(graph)) {
if (graph.__refresh__) {
graph.draw(opts.ctx)
} else {
graph.setCacheSnapshot(opts.c)
}
}

if (asserts.isGraph(graph)) {
const matrix = graph.matrix.create({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 })
matrix.transform(graph.x, graph.y, graph.scaleX, graph.scaleY, graph.rotation, graph.skewX, graph.skewY)
applyCanvasTransform(this.render.ctx, matrix, this.render.options.devicePixelRatio)
graph.render(render.ctx)
}
}
render.ctx.restore()
})
}
}
147 changes: 80 additions & 67 deletions src/primitives/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { createFillBlock, mixin } from '../shared'
import { Display, S } from '../etoile/graph/display'
import { Render, Schedule, Event as _Event, easing, etoile } from '../etoile'
import { Schedule, Event as _Event, easing, etoile } from '../etoile'
import type { BindThisParameter } from '../etoile'
import type { ColorDecoratorResultRGB } from '../etoile/native/runtime'
import type { InheritedCollections } from '../shared'
Expand Down Expand Up @@ -66,15 +66,15 @@ function smoothDrawing(c: SelfEventContenxt) {
const { self } = c
const currentNode = self.currentNode
if (currentNode) {
const { run, stop } = createEffectScope()
const effect = createEffectScope()
const startTime = Date.now()
const animationDuration = 300
const [x, y, w, h] = currentNode.layout
run(() => {
effect.run(() => {
const elapsed = Date.now() - startTime
const progress = Math.min(elapsed / animationDuration, 1)
if (self.forceDestroy || progress >= 1) {
stop()
effect.stop()
self.highlight.reset()
self.highlight.setDisplayLayerForHighlight()
return true
Expand All @@ -85,7 +85,7 @@ function smoothDrawing(c: SelfEventContenxt) {
self.highlight.highlight.add(mask)
self.highlight.setDisplayLayerForHighlight('1')
applyForOpacity(mask, 0.4, 0.4, easedProgress)
setupGraphTransform(mask, self.translateX, self.translateY, self.scaleRatio)
stackMatrixTransform(mask, self.translateX, self.translateY, self.scaleRatio)
self.highlight.highlight.update()
})
} else {
Expand Down Expand Up @@ -173,6 +173,7 @@ export class SelfEvent extends RegisterModule {
isDragging: boolean
draggingState: DraggingState
event: _Event<SelfEventDefinition>
triggerZoom: boolean
// eslint-disable-next-line no-use-before-define
highlight: HighlightContext
constructor() {
Expand All @@ -187,6 +188,7 @@ export class SelfEvent extends RegisterModule {
this.layoutHeight = 0
this.draggingState = { x: 0, y: 0 }
this.event = new _Event<SelfEventDefinition>()
this.triggerZoom = false
this.highlight = createHighlight()
}

Expand Down Expand Up @@ -227,14 +229,16 @@ export class SelfEvent extends RegisterModule {
this.self.translateX += drawX
this.self.translateY += drawY
this.self.draggingState = { x, y }
if (this.self.triggerZoom) {
refreshBackgroundLayer(this)
}
this.treemap.reset()
setupGraphTransform(this.treemap.backgroundLayer, 0, 0, 0)
applyGraphTransform(this.treemap.elements, this.self.translateX, this.self.translateY, this.self.scaleRatio)
setupGraphTransform(this.treemap.backgroundLayer, this.self.translateX, this.self.translateY, this.self.scaleRatio)
stackMatrixTransform(this.treemap.backgroundLayer, 0, 0, 0)
stackMatrixTransformWithGraphAndLayer(this.treemap.elements, this.self.translateX, this.self.translateY, this.self.scaleRatio)
this.treemap.update()
}

ondragend(this: SelfEventContenxt, metadata: PrimitiveEventMetadata<'mousemove'>) {
ondragend(this: SelfEventContenxt) {
if (!this.self.isDragging) {
return
}
Expand All @@ -255,7 +259,7 @@ export class SelfEvent extends RegisterModule {
smoothDrawing(this)
}

onmouseout(this: SelfEventContenxt, metadata: PrimitiveEventMetadata<'mouseout'>) {
onmouseout(this: SelfEventContenxt) {
const { self } = this
self.currentNode = null
self.forceDestroy = true
Expand All @@ -274,26 +278,27 @@ export class SelfEvent extends RegisterModule {
return
}
self.forceDestroy = true
if (self.triggerZoom) {
refreshBackgroundLayer(this)
}
treemap.reset()
this.self.highlight.reset()
this.self.highlight.setDisplayLayerForHighlight()
setupGraphTransform(treemap.backgroundLayer, 0, 0, 0)
stackMatrixTransform(this.treemap.backgroundLayer, 0, 0, 0)
const factor = absWheelDelta > 3 ? 1.4 : absWheelDelta > 1 ? 1.2 : 1.1
const delta = wheelDelta > 0 ? factor : 1 / factor

self.scaleRatio *= delta

const translateX = offsetX - (offsetX - self.translateX) * delta
const translateY = offsetY - (offsetY - self.translateY) * delta
self.translateX = translateX
self.translateY = translateY
applyGraphTransform(treemap.elements, self.translateX, self.translateY, self.scaleRatio)
setupGraphTransform(treemap.backgroundLayer, self.translateX, self.translateY, self.scaleRatio)
stackMatrixTransformWithGraphAndLayer(this.treemap.elements, this.self.translateX, this.self.translateY, this.self.scaleRatio)
treemap.update()
self.forceDestroy = false
}

init(app: App, treemap: TreemapLayout, render: Render): void {
init(app: App, treemap: TreemapLayout): void {
const event = this.event
const nativeEvents: Array<ReturnType<typeof bindPrimitiveEvent>> = []
const methods: InheritedCollections[] = [
Expand Down Expand Up @@ -350,6 +355,7 @@ export class SelfEvent extends RegisterModule {
this.layoutWidth = treemap.render.canvas.width
this.layoutHeight = treemap.render.canvas.height
this.isDragging = false
this.triggerZoom = false
this.draggingState = { x: 0, y: 0 }
})
}
Expand Down Expand Up @@ -389,65 +395,65 @@ function estimateZoomingArea(node: LayoutModule, root: LayoutModule | null, w: n
return [w * scaleFactor, h * scaleFactor]
}

function setupGraphTransform(graph: S, translateX: number, translateY: number, scale: number) {
graph.x = graph.x * scale + translateX
graph.y = graph.y * scale + translateY
function stackMatrixTransform(graph: S, e: number, f: number, scale: number) {
graph.x = graph.x * scale + e
graph.y = graph.y * scale + f
graph.scaleX = scale
graph.scaleY = scale
}

function applyGraphTransform(graphs: Display[], translateX: number, translateY: number, scale: number) {
etoile.traverse(graphs, (graph) => setupGraphTransform(graph, translateX, translateY, scale))
function stackMatrixTransformWithGraphAndLayer(graphs: Display[], e: number, f: number, scale: number) {
etoile.traverse(graphs, (graph) => stackMatrixTransform(graph, e, f, scale))
}

function onZoom(ctx: SelfEventContenxt, node: LayoutModule, root: LayoutModule | null) {
// if (!node) return
// const { treemap, self } = ctx
// self.forceDestroy = true
// const c = treemap.render.canvas
// const boundingClientRect = c.getBoundingClientRect()
// const [w, h] = estimateZoomingArea(node, root, boundingClientRect.width, boundingClientRect.height)
// resetLayout(treemap, w, h)
// const module = findRelativeNodeById(node.node.id, treemap.layoutNodes)
// if (module) {
// const [mx, my, mw, mh] = module.layout
// const scale = Math.min(boundingClientRect.width / mw, boundingClientRect.height / mh)
// const translateX = (boundingClientRect.width / 2) - (mx + mw / 2) * scale
// const translateY = (boundingClientRect.height / 2) - (my + mh / 2) * scale
// const initialScale = self.scaleRatio
// const initialTranslateX = self.translateX
// const initialTranslateY = self.translateY
// const startTime = Date.now()
// const animationDuration = 300
// if (self.layoutHeight !== w || self.layoutHeight !== h) {
// // remove font caches
// delete treemap.fontsCaches[module.node.id]
// delete treemap.ellispsisWidthCache[module.node.id]
// }
// const { run, stop } = createEffectScope()
// run(() => {
// const elapsed = Date.now() - startTime
// const progress = Math.min(elapsed / animationDuration, 1)
// treemap.backgroundLayer.__refresh__ = false
// // setupGraphTransform(treemap.backgroundLayer, 0, 0, 0)
// if (progress >= 1) {
// stop()
// self.layoutWidth = w
// self.layoutHeight = h
// self.forceDestroy = false
// return true
// }
// const easedProgress = easing.cubicInOut(progress)
// const scaleRatio = initialScale + (scale - initialScale) * easedProgress
// self.translateX = initialTranslateX + (translateX - initialTranslateX) * easedProgress
// self.translateY = initialTranslateY + (translateY - initialTranslateY) * easedProgress
// self.scaleRatio = scaleRatio
// treemap.reset()
// applyGraphTransform(treemap.elements, self.translateX, self.translateY, scaleRatio)
// treemap.update()
// })
// }
// root = node
if (!node) return
const { treemap, self } = ctx
self.forceDestroy = true
const c = treemap.render.canvas
const boundingClientRect = c.getBoundingClientRect()
const [w, h] = estimateZoomingArea(node, root, boundingClientRect.width, boundingClientRect.height)
resetLayout(treemap, w, h)
const module = findRelativeNodeById(node.node.id, treemap.layoutNodes)
if (module) {
const [mx, my, mw, mh] = module.layout
const scale = Math.min(boundingClientRect.width / mw, boundingClientRect.height / mh)
const translateX = (boundingClientRect.width / 2) - (mx + mw / 2) * scale
const translateY = (boundingClientRect.height / 2) - (my + mh / 2) * scale
const initialScale = self.scaleRatio
const initialTranslateX = self.translateX
const initialTranslateY = self.translateY
const startTime = Date.now()
const animationDuration = 300
if (self.layoutHeight !== w || self.layoutHeight !== h) {
// remove font caches
delete treemap.fontsCaches[module.node.id]
delete treemap.ellispsisWidthCache[module.node.id]
}
const { run, stop } = createEffectScope()
run(() => {
const elapsed = Date.now() - startTime
const progress = Math.min(elapsed / animationDuration, 1)
treemap.backgroundLayer.__refresh__ = false
if (progress >= 1) {
stop()
self.layoutWidth = w
self.layoutHeight = h
self.forceDestroy = false
self.triggerZoom = true
return true
}
const easedProgress = easing.cubicInOut(progress)
const scaleRatio = initialScale + (scale - initialScale) * easedProgress
self.translateX = initialTranslateX + (translateX - initialTranslateX) * easedProgress
self.translateY = initialTranslateY + (translateY - initialTranslateY) * easedProgress
self.scaleRatio = scaleRatio
treemap.reset()
stackMatrixTransformWithGraphAndLayer(treemap.elements, self.translateX, self.translateY, scaleRatio)
treemap.update()
})
}
root = node
}

interface DuckE {
Expand Down Expand Up @@ -499,3 +505,10 @@ function createHighlight(): HighlightContext {
}
}
}

// TODO: cache the background layer
function refreshBackgroundLayer(c: SelfEventContenxt) {
const { treemap } = c
const { backgroundLayer } = treemap
backgroundLayer.__refresh__ = false
}

0 comments on commit 4755ec2

Please sign in to comment.