Skip to content

Commit

Permalink
perf: make schedule happy
Browse files Browse the repository at this point in the history
  • Loading branch information
nonzzz committed Nov 13, 2024
1 parent 39d7035 commit 2f0a490
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 29 deletions.
10 changes: 6 additions & 4 deletions src/etoile/etoile.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { Box } from './graph'
import { asserts } from './graph'
import { Display, Graph } from './graph/display'
import { Schedule } from './schedule'

function traverse(graphs: Display[], handler: (graph: Graph) => void) {
graphs.forEach(graph => {
if (graph instanceof Box) {
const len = graphs.length
for (let i = 0; i < len; i++) {
const graph = graphs[i]
if (asserts.isBox(graph)) {
traverse(graph.elements, handler)
} else if (graph instanceof Graph) {
handler(graph)
}
})
}
}

export const etoile = {
Expand Down
33 changes: 32 additions & 1 deletion src/etoile/graph/box.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Display } from './display'
import { Display, DisplayType } from './display'
import { asserts } from './types'

export class Box extends Display {
elements: Display[]
Expand Down Expand Up @@ -37,4 +38,34 @@ export class Box extends Display {
this.elements.forEach(element => element.parent = null)
this.elements.length = 0
}

get __instanceOf__(): DisplayType.Box {
return DisplayType.Box
}

clone() {
const box = new Box()
if (this.elements.length) {
const traverse = (elements: Display[], parent: Box) => {
const els: Display[] = []
const cap = elements.length
for (let i = 0; i < cap; i++) {
const element = elements[i]
if (asserts.isBox(element)) {
const box = new Box()
box.parent = parent
box.add(...traverse(element.elements, box))
els.push(box)
} else if (asserts.isGraph(element)) {
const el = element.clone()
el.parent = parent
els.push(el)
}
}
return els
}
box.add(...traverse(this.elements, box))
}
return box
}
}
44 changes: 36 additions & 8 deletions src/etoile/graph/display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,22 @@ const SELF_ID = {
}
}

export class Display {
export const enum DisplayType {
// eslint-disable-next-line no-unused-vars
Graph = 'Graph',
// eslint-disable-next-line no-unused-vars
Box = 'Box',
// eslint-disable-next-line no-unused-vars
Rect = 'Rect',
// eslint-disable-next-line no-unused-vars
Text = 'Text'
}

export abstract class Display {
parent: Display | null
id: number
matrix: Matrix2D
abstract get __instanceOf__(): string
constructor() {
this.parent = null
this.id = SELF_ID.get()
Expand Down Expand Up @@ -103,7 +115,7 @@ function createInstruction() {
}
}

export class S extends Display {
export abstract class S extends Display {
width: number
height: number
x: number
Expand All @@ -129,23 +141,39 @@ export class S extends Display {

export abstract class Graph extends S {
instruction: ReturnType<typeof createInstruction>
__refresh__: boolean
__options__: Partial<LocOptions>
abstract style: GraphStyleSheet
constructor(options: Partial<GraphOptions> = {}) {
super(options)
this.instruction = createInstruction()
// For better performance
this.__refresh__ = true
this.__options__ = options
}
abstract create(): void
abstract clone(): Graph
abstract get __shape__(): string

render(ctx: CanvasRenderingContext2D) {
this.create()
this.instruction.mods.forEach((mod) => {
const direct = mod[0]
const cap = this.instruction.mods.length

for (let i = 0; i < cap; i++) {
const mod = this.instruction.mods[i]
const [direct, ...args] = mod
if (direct in ASSIGN_MAPPINGS) {
// @ts-expect-error
ctx[direct] = mod[1]
return
ctx[direct] = args[0]
continue
}

// @ts-expect-error
ctx[direct].apply(ctx, ...mod.slice(1))
})
ctx[direct].apply(ctx, ...args)
}
}

get __instanceOf__(): DisplayType.Graph {
return DisplayType.Graph
}
}
1 change: 1 addition & 0 deletions src/etoile/graph/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { Box } from './box'
export { Rect } from './rect'
export { Text } from './text'
export * from './types'
12 changes: 10 additions & 2 deletions src/etoile/graph/rect.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { runtime } from '../native/runtime'
import type { ColorDecoratorResult } from '../native/runtime'
import { Graph, GraphStyleSheet } from './display'
import type { GraphOptions } from './display'
import { DisplayType, Graph } from './display'
import type { GraphOptions, GraphStyleSheet } from './display'

export type RectStyleOptions = GraphStyleSheet & { fill: ColorDecoratorResult }

Expand All @@ -13,6 +13,10 @@ export class Rect extends Graph {
this.style = options.style || Object.create(null)
}

get __shape__() {
return DisplayType.Rect
}

create() {
if (this.style.fill) {
this.instruction.fillStyle(runtime.evaluateFillStyle(this.style.fill, this.style.opacity))
Expand All @@ -26,4 +30,8 @@ export class Rect extends Graph {
this.instruction.strokeRect(0, 0, this.width, this.height)
}
}

clone() {
return new Rect({ ...this.style, ...this.__options__ })
}
}
10 changes: 9 additions & 1 deletion src/etoile/graph/text.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Graph } from './display'
import { DisplayType, Graph } from './display'
import type { GraphOptions, GraphStyleSheet } from './display'

export interface TextOptions extends Omit<GraphOptions, 'style'> {
Expand Down Expand Up @@ -32,4 +32,12 @@ export class Text extends Graph {
this.instruction.fillText(this.text, 0, 0)
}
}

clone() {
return new Text({ ...this.style, ...this.__options__ })
}

get __shape__() {
return DisplayType.Text
}
}
27 changes: 27 additions & 0 deletions src/etoile/graph/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Box } from './box'
import { Display, DisplayType, Graph } from './display'
import { Rect } from './rect'
import { Text } from './text'

export function isGraph(display: Display): display is Graph {
return display.__instanceOf__ === DisplayType.Graph
}

export function isBox(display: Display): display is Box {
return display.__instanceOf__ === DisplayType.Box
}

export function isRect(display: Display): display is Rect {
return isGraph(display) && display.__shape__ === DisplayType.Rect
}

export function isText(display: Display): display is Text {
return isGraph(display) && display.__shape__ === DisplayType.Text
}

export const asserts = {
isGraph,
isBox,
isRect,
isText
}
29 changes: 18 additions & 11 deletions src/etoile/schedule/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box } from '../graph'
import { Display, Graph } from '../graph/display'
import { Box, asserts } from '../graph'
import { Display } from '../graph/display'
import { Event } from '../native/event'
import { log } from '../native/log'
import { Matrix2D } from '../native/matrix'
Expand Down Expand Up @@ -46,22 +46,29 @@ export class Schedule extends Box {
// execute all graph elements
execute(render: Render, graph: Display = this) {
render.ctx.save()
let matrix = graph.matrix
this.applyTransform(matrix)
if (graph instanceof Box) {
const cap = graph.elements.length
if (asserts.isBox(graph)) {
const elements = graph.elements
const cap = elements.length
const matrices = new Array(cap)
for (let i = 0; i < cap; i++) {
const element = graph.elements[i]
matrix = element.matrix.create({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 })
if (element instanceof Graph) {
matrix.transform(element.x, element.y, element.scaleX, element.scaleY, element.rotation, element.skewX, element.skewY)
const element = elements[i]
if (asserts.isGraph(element)) {
matrices[i] = element.matrix.create({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 })
matrices[i].transform(element.x, element.y, element.scaleX, element.scaleY, element.rotation, element.skewX, element.skewY)
}
}
for (let i = 0; i < cap; i++) {
const element = elements[i]
this.execute(render, element)
}
}
if (graph instanceof Graph) {

if (asserts.isGraph(graph)) {
const matrix = graph.matrix
this.applyTransform(matrix)
graph.render(render.ctx)
}

render.ctx.restore()
}
}
4 changes: 2 additions & 2 deletions src/primitives/animation.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Rect } from '../etoile'
import { asserts } from '../etoile'
import { Graph } from '../etoile/graph/display'

export function applyForOpacity(graph: Graph, lastState: number, nextState: number, easedProgress: number) {
const alpha = lastState + (nextState - lastState) * easedProgress
if (graph instanceof Rect) {
if (asserts.isRect(graph)) {
graph.style.opacity = alpha
}
}

0 comments on commit 2f0a490

Please sign in to comment.