Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proof of concept: publisher-subscriber model for asset loading #96

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,18 @@ export const DEFAULT_TEXT_STYLE = {
WORD_WRAP: false as const,
FONT_STYLE: 'normal' as const
}

export const GENERIC_FONT_FAMILIES = new Set([
'serif',
'sans-serif',
'monospace',
'cursive',
'fantasy',
'system-ui',
'emoji',
'math',
'fangsong',
'ui-serif',
'ui-sans-serif',
'ui-monospace'
])
2 changes: 2 additions & 0 deletions src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,5 @@ export const interpolate = (from: number, to: number, duration: number) => {
return { done: false, value: ease(elapsed / duration) }
}
}

export const warn = throttle((err) => console.warn(err), 0)
13 changes: 7 additions & 6 deletions src/webgl/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Application, Container, EventSystem, FederatedPointerEvent, Rectangle } from 'pixi.js'
import { ArrowTexture, CircleTexture, TextIconTexture, ImageIconTexture } from './textures'
import { ArrowTexture, CircleTexture } from './textures'
import { NodeRenderer, EdgeRenderer, ObjectManager } from './objects'
import { interpolate, logUnknownEdgeError } from '../utils'
import {
Expand All @@ -20,8 +20,10 @@ import {
} from '../types'
import { Zoom, Drag, Decelerate } from './interactions'
import { Grid } from './grid'
import FontBook from './textures/text/FontBook'
import FontBook from './textures/assets/FontBook'
import Stats from 'stats.js'
import TextIconCache from './textures/icons/TextIconCache'
import AssetLoader from './textures/assets/AssetLoader'

export const defaultOptions = {
x: 0,
Expand Down Expand Up @@ -81,8 +83,8 @@ export class Renderer {
animateNodeRadius: number | false = defaultOptions.animateNodeRadius
circle: CircleTexture
arrow: ArrowTexture
textIcon: TextIconTexture
imageIcon: ImageIconTexture
textIcon: TextIconCache
assets = new AssetLoader()
fontBook = new FontBook() // TODO -> make configurable
draggedNode?: NodeRenderer
hoveredNode?: NodeRenderer
Expand Down Expand Up @@ -161,8 +163,7 @@ export class Renderer {
this.app.stage.addChild(this.root)
this.circle = new CircleTexture(this)
this.arrow = new ArrowTexture(this)
this.textIcon = new TextIconTexture(this)
this.imageIcon = new ImageIconTexture()
this.textIcon = new TextIconCache(this)
this.eventSystem = new EventSystem(this.app.renderer)
this.eventSystem.domElement = view
this.root.eventMode = 'static' // 'passive' // TODO - add viewport events to interactionContainer
Expand Down
15 changes: 1 addition & 14 deletions src/webgl/objects/edge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,7 @@ export class EdgeRenderer {
this.labelMounted = false
}
} else if (this.label === undefined) {
Text.init(this.renderer.fontBook, this.renderer.labelsContainer, edge.label, edge.style?.label).then((label) => {
if (label) {
this.label = label
this.label.rotation = this.theta
this.label.moveTo(...this.center)
if (
this.renderer.zoom > MIN_LABEL_ZOOM &&
this.visible(Math.min(this.x0, this.x1), Math.min(this.y0, this.y1), Math.max(this.x0, this.x1), Math.max(this.y0, this.y1))
) {
this.labelMounted = true
this.renderer.labelObjectManager.mount(this.label)
}
}
})
this.label = new Text(this.renderer.fontBook, this.renderer.labelsContainer, edge.label, edge.style?.label)
} else {
this.label.update(edge.label, edge.style?.label)
}
Expand Down
130 changes: 0 additions & 130 deletions src/webgl/objects/node/icon.ts

This file was deleted.

28 changes: 10 additions & 18 deletions src/webgl/objects/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { type Renderer } from '../..'
import { Node } from './../../../types'
import { NodeStrokes } from './strokes'
import { NodeFill } from './fill'
import { Icon } from './icon'
import { NodeHitArea } from './hitArea'
import Text from '../../textures/text/Text'
import Icon from '../../textures/icons/Icon'

export class NodeRenderer {
x = 0
Expand Down Expand Up @@ -58,16 +58,7 @@ export class NodeRenderer {
this.label = undefined
}
} else if (this.label === undefined) {
this.labelLoading = true
Text.init(this.renderer.fontBook, this.renderer.labelsContainer, nodeLabel, labelStyle).then((label) => {
if (label) {
this.label = label
this.labelLoading = false
this.label.offset = this.strokes.radius
this.label.moveTo(this.x, this.y)
this.mountLabel(this.visible() && this.renderer.zoom > MIN_LABEL_ZOOM)
}
})
this.label = new Text(this.renderer.fontBook, this.renderer.labelsContainer, nodeLabel, labelStyle)
} else {
this.label.update(nodeLabel, labelStyle)
}
Expand All @@ -80,13 +71,14 @@ export class NodeRenderer {
this.icon = undefined
}
} else if (this.icon === undefined) {
this.iconLoading = true
Icon.init(this.renderer.nodesContainer, this.renderer.textIcon, this.renderer.imageIcon, this.fill, iconStyle).then((icon) => {
this.icon = icon
this.iconLoading = false
this.icon?.moveTo(this.x, this.y)
this.mountIcon(this.visible() && this.renderer.zoom > MIN_NODE_ICON_ZOOM)
})
this.icon = new Icon(
this.renderer.nodesContainer,
this.renderer.fontBook,
this.renderer.textIcon,
this.renderer.assets,
this.fill,
iconStyle
)
} else {
this.icon.update(iconStyle)
}
Expand Down
60 changes: 60 additions & 0 deletions src/webgl/textures/abstracts/PubSub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { warn } from '../../../utils'

export interface Subscriber<T> {
(asset: T): void
}

export interface Subscription {
ready: boolean
unsubscribe(): void
}

export default abstract class Publisher<T, S extends Subscription> {
ready = false
asset: T | null = null
subscribers = new Set<Subscriber<T>>()

protected abstract caller(): Promise<T>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldn't think of a better name for this method lol

protected abstract subscription(subscriber: Subscriber<T>): S

subscribe(subscriber: Subscriber<T>) {
if (this.ready && this.asset !== null) {
subscriber(this.asset)
} else {
this.subscribers.add(subscriber)
}

return this.subscription(subscriber)
}

unsubscribe(fn: Subscriber<T>) {
this.subscribers.delete(fn)
return undefined
}

delete() {
this.subscribers = new Set()
return undefined
}

protected async load() {
try {
this.asset = await this.caller()
this.ready = true
this.notify(this.asset)
} catch (error) {
warn(error)
this.asset = null
this.ready = false
this.delete()
}
}

protected notify(asset: T) {
for (const fn of this.subscribers) {
fn(asset)
}

this.subscribers = new Set()
}
}
42 changes: 42 additions & 0 deletions src/webgl/textures/abstracts/TextureAbstract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MSAA_QUALITY, RenderTexture, Renderer as PixiRenderer, IRenderableObject, IRendererRenderOptions } from 'pixi.js'
import { MIN_ZOOM } from '../../../utils'
import { Renderer } from '../..'

export default abstract class TextureAbstract {
resolution = 2
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: plug in to Renderer options

scaleFactor = MIN_ZOOM

constructor(protected renderer: Renderer) {
this.renderer = renderer
}

abstract delete(): void

protected createRenderTexture(width: number, height: number) {
return RenderTexture.create({
width,
height,
resolution: this.resolution,
multisample: MSAA_QUALITY.HIGH
})
}

protected render<T extends IRenderableObject>(graphic: T, options?: IRendererRenderOptions) {
this.renderer.app.renderer.render(graphic, options)

return this
}

protected blit() {
if (this.renderer.app.renderer instanceof PixiRenderer) {
this.renderer.app.renderer.framebuffer.blit()
}

return this
}

protected destroy<T extends { destroy: (options?: boolean) => void }>(graphic: T) {
graphic.destroy(true)
return this
}
}
11 changes: 11 additions & 0 deletions src/webgl/textures/abstracts/TextureCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default abstract class TextureCache<T extends { delete: () => void }> {
protected cache: { [key: string]: T } = {}

delete() {
for (const key in this.cache) {
this.cache[key].delete()
}

this.cache = {}
}
}
Loading