diff --git a/src/cull.ts b/src/cull.ts new file mode 100644 index 0000000..a34e6f3 --- /dev/null +++ b/src/cull.ts @@ -0,0 +1,42 @@ +import { DisplayObjectWithCulling, Simple } from 'pixi-cull'; +// import { Point } from '@pixi/math'; +// import { Bounds } from '@pixi/display'; + +// refer: https://github.com/davidfig/pixi-cull/issues/2#issuecomment-570087695 +export default class Cull extends Simple { + updateObject(object: DisplayObjectWithCulling) { + // original code + // const box = object.getLocalBounds(); + // object.AABB = object.AABB || { x: 0, y: 0, width: 0, height: 0 }; + // object.AABB.x = object.x + (box.x - object.pivot.x) * Math.abs(object.scale.x); + // object.AABB.y = object.y + (box.y - object.pivot.y) * Math.abs(object.scale.y); + // object.AABB.width = box.width * Math.abs(object.scale.x); + // object.AABB.height = box.height * Math.abs(object.scale.y); + + const box = object.getBounds(); + object.AABB = box; + + // const rotPivot = new Point(object.x + object.pivot.x, object.y + object.pivot.y); + // const rotSin = Math.sin(object.rotation); + // const rotCos = Math.cos(object.rotation); + + // const points = [ + // new Point(object.x, object.y), + // new Point(object.x + box.width, object.y), + // new Point(object.x, object.y + box.height), + // new Point(object.x + box.width, object.y + box.height), + // ]; + // const pointsTransformed = points.map((point) => { + // return new Point( + // (point.x - rotPivot.x) * rotCos - (point.y - rotPivot.y) * rotSin + rotPivot.x, + // (point.x - rotPivot.x) * rotSin + (point.y - rotPivot.y) * rotCos + rotPivot.y + // ); + // }); + + // const bounds = new Bounds(); + // pointsTransformed.map((point) => bounds.addPoint(point)); + // const aabb = bounds.getRectangle(); + + // object.AABB = aabb; + } +} diff --git a/src/graph.ts b/src/graph.ts index 00df186..9c40336 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -8,8 +8,7 @@ import { Container } from '@pixi/display'; import { Point, IPointData, Rectangle } from '@pixi/math'; import { IAddOptions } from '@pixi/loaders'; import { Viewport } from 'pixi-viewport'; -import { Cull } from '@pixi-essentials/cull'; -// import { Simple } from 'pixi-cull'; +import Cull from './cull'; import { AbstractGraph } from 'graphology-types'; import { TypedEmitter } from 'tiny-typed-emitter'; import { GraphStyleDefinition, NodeStyleDefinition, resolveStyleDefinitions } from './utils/style'; @@ -101,7 +100,7 @@ export class PixiGraph< private app: Application; private textureCache: TextureCache; private viewport: Viewport; - // private cull: Simple; + private cull: Cull; private resizeObserver: ResizeObserver; private edgeLayer: Container; private frontEdgeLayer: Container; @@ -158,9 +157,9 @@ export class PixiGraph< autoDensity: true, }); this.container.appendChild(this.app.view); - // this.cull = new Simple({ - // dirtyTest: true, - // }); + this.cull = new Cull({ + dirtyTest: false, + }); this.app.renderer.plugins.interaction.moveWhenInside = true; this.app.view.addEventListener('wheel', (event) => { @@ -238,6 +237,7 @@ export class PixiGraph< // preload resources if (this.resources) { + // @ts-ignore this.app.loader.add(this.resources); } this.app.loader.load(() => { @@ -529,10 +529,17 @@ export class PixiGraph< const node = this.nodeKeyToNodeObject.get(nodeKey)!; const nodePosition = { x: point.x, y: point.y }; node.updatePosition(nodePosition); + // @ts-ignore + this.cull.updateObject(node.nodeGfx); // update style this.updateNodeStyleByKey(nodeKey); this.graph.edges(nodeKey).forEach(this.updateEdgeStyleByKey.bind(this)); + this.graph.edges(nodeKey).forEach((edgeKey: string) => { + const edge = this.edgeKeyToEdgeObject.get(edgeKey)!; + // @ts-ignore + this.cull.updateObject(edge.edgeGfx); + }); } private moveNodebyDelta(nodeKey: string, deltaX: number, deltaY: number) { @@ -545,10 +552,17 @@ export class PixiGraph< const node = this.nodeKeyToNodeObject.get(nodeKey)!; const nodePosition = { x: x + deltaX, y: y + deltaY }; node.updatePosition(nodePosition); + // @ts-ignore + this.cull.updateObject(node.nodeGfx); // update style this.updateNodeStyleByKey(nodeKey); this.graph.edges(nodeKey).forEach(this.updateEdgeStyleByKey.bind(this)); + this.graph.edges(nodeKey).forEach((edgeKey: string) => { + const edge = this.edgeKeyToEdgeObject.get(edgeKey)!; + // @ts-ignore + this.cull.updateObject(edge.edgeGfx); + }); } private enableNodeDragging() { @@ -588,11 +602,8 @@ export class PixiGraph< this.graph.forEachNode(this.createNode.bind(this)); this.graph.forEachEdge(this.createEdge.bind(this)); - // todo - // when graph change(position change or add/delete new node) - // should mark related object dirty. // @ts-ignore - // (this.viewport.children as Container[]).map((layer) => this.cull.addList(layer.children)); + (this.viewport.children as Container[]).map((layer) => this.cull.addList(layer.children)); } private createNode(nodeKey: string, nodeAttributes: NodeAttributes) { @@ -685,6 +696,8 @@ export class PixiGraph< const nodePosition = { x: nodeAttributes.x, y: nodeAttributes.y }; node.updatePosition(nodePosition); + // @ts-ignore + this.cull.updateObject(node.nodeGfx); this.updateNodeStyle(nodeKey, nodeAttributes); } @@ -826,6 +839,8 @@ export class PixiGraph< parallelEdgeCount, parallelSeq ); + // @ts-ignore + this.cull.updateObject(edge.edgeGfx); edge.updateStyle( edgeStyle, @@ -840,13 +855,13 @@ export class PixiGraph< private updateGraphVisibility() { // culling todo(rotation cull have bug) // https://github.com/davidfig/pixi-cull/issues/2 - // this.cull.cull(this.viewport.getVisibleBounds(), false); + this.cull.cull(this.viewport.getVisibleBounds(), true); // should refer https://github.com/ShukantPal/pixi-essentials/tree/master/packages/cull // original culling have performance issue. - const cull = new Cull(); - cull.addAll((this.viewport.children as Container[]).map((layer) => layer.children).flat()); - cull.cull(this.app.renderer.screen); + // const cull = new Cull(); + // cull.addAll((this.viewport.children as Container[]).map((layer) => layer.children).flat()); + // cull.cull(this.app.renderer.screen); // console.log( // Array.from((cull as any)._targetList as Set).filter(x => x.visible === true).length, @@ -859,13 +874,15 @@ export class PixiGraph< const zoomStep = zoomSteps.findIndex((zoomStep) => zoom <= zoomStep); console.log(zoom, zoomStep); + // edge (line) will always show + // zoomStep = 0, zoom <= 0.1 // node background // zoomStep = 1, 0.1 < zoom <= 0.2 // node border // zoomStep = 2, 0.2 < zoom <= 0.4 // node icon - // edge (line/parallel edge/self loop edge) + // edge (parallel edge/self loop edge) // zoomStep = 3, 0.4 < zoom < Infinity // node label // edge arrow diff --git a/src/renderers/edge.ts b/src/renderers/edge.ts index b9a1b51..37a8bed 100644 --- a/src/renderers/edge.ts +++ b/src/renderers/edge.ts @@ -70,10 +70,10 @@ export function updatePosition( const [color, alpha] = colorToPixi(edgeStyle.color); const length = Math.hypot(targetNodePosition.x - sourceNodePosition.x, targetNodePosition.y - sourceNodePosition.y); - const edgeLine = edgeGfx.getChildByName!(EDGE_LINE) as Sprite; - const edgeArrow = edgeGfx.getChildByName!(EDGE_ARROW) as Sprite; - const edgeCurve = edgeGfx.getChildByName!(EDGE_CURVE) as Graphics; - const edgeCurveArrow = edgeGfx.getChildByName!(EDGE_CURVE_ARROW) as Graphics; + const edgeLine = edgeGfx.getChildByName!(EDGE_LINE) as unknown as Sprite; + const edgeArrow = edgeGfx.getChildByName!(EDGE_ARROW) as unknown as Sprite; + const edgeCurve = edgeGfx.getChildByName!(EDGE_CURVE) as unknown as Graphics; + const edgeCurveArrow = edgeGfx.getChildByName!(EDGE_CURVE_ARROW) as unknown as Graphics; edgeLine.visible = false; edgeArrow.visible = false; @@ -194,7 +194,7 @@ export function updateEdgeStyle( } if (parallelEdgeCount <= 1 || (parallelEdgeCount % 2 === 1 && parallelSeq === parallelEdgeCount)) { // edgeGfx -> edgeLine - const edgeLine = edgeGfx.getChildByName!(EDGE_LINE) as Sprite; + const edgeLine = edgeGfx.getChildByName!(EDGE_LINE) as unknown as Sprite; edgeLine.height = edgeStyle.width; [edgeLine.tint, edgeLine.alpha] = colorToPixi(edgeStyle.color); } @@ -207,14 +207,14 @@ export function updateEdgeVisibility( parallelEdgeCount: number, parallelSeq: number ) { - const edgeLine = edgeGfx.getChildByName!(EDGE_LINE) as Sprite; - const edgeArrow = edgeGfx.getChildByName!(EDGE_ARROW) as Sprite; - const edgeCurve = edgeGfx.getChildByName!(EDGE_CURVE) as Graphics; - const edgeCurveArrow = edgeGfx.getChildByName!(EDGE_CURVE_ARROW) as Graphics; + const edgeLine = edgeGfx.getChildByName!(EDGE_LINE) as unknown as Sprite; + const edgeArrow = edgeGfx.getChildByName!(EDGE_ARROW) as unknown as Sprite; + const edgeCurve = edgeGfx.getChildByName!(EDGE_CURVE) as unknown as Graphics; + const edgeCurveArrow = edgeGfx.getChildByName!(EDGE_CURVE_ARROW) as unknown as Graphics; if (isSelfLoop) { // edgeGfx -> edgeCurve - edgeCurve.visible = zoomStep >= 2; + edgeCurve.visible = zoomStep >= 3; // edgeGfx -> edgeCurveArrow edgeCurveArrow.visible = zoomStep >= 3; @@ -226,7 +226,10 @@ export function updateEdgeVisibility( if (parallelEdgeCount <= 1 || (parallelEdgeCount % 2 === 1 && parallelSeq === parallelEdgeCount)) { // edgeGfx -> edgeLine - edgeLine.visible = zoomStep >= 2; + // edgeLine.visible = zoomStep >= 1; + // todo(lin): we may need display line all the time. + edgeLine.visible = true; + // edgeGFX -> edgeArrow edgeArrow.visible = zoomStep >= 3; @@ -235,7 +238,7 @@ export function updateEdgeVisibility( edgeCurveArrow.visible = false; } else { // edgeGfx -> edgeCurve - edgeCurve.visible = zoomStep >= 2; + edgeCurve.visible = zoomStep >= 3; // edgeGfx -> edgeCurveArrow edgeCurveArrow.visible = zoomStep >= 3; diff --git a/src/renderers/node-label.ts b/src/renderers/node-label.ts index 9a8eafc..be913e3 100644 --- a/src/renderers/node-label.ts +++ b/src/renderers/node-label.ts @@ -35,7 +35,7 @@ export function updateNodeLabelStyle(nodeLabelGfx: Container, nodeStyle: NodeSty }); // nodeLabelGfx -> nodeLabelText - const nodeLabelText = nodeLabelGfx.getChildByName!(NODE_LABEL_TEXT) as Sprite; + const nodeLabelText = nodeLabelGfx.getChildByName!(NODE_LABEL_TEXT) as unknown as Sprite; nodeLabelText.texture = nodeLabelTextTexture; nodeLabelText.y = nodeStyle.size + (nodeLabelTextTexture.height + nodeStyle.label.padding * 2) / 2; [nodeLabelText.tint, nodeLabelText.alpha] = colorToPixi(nodeStyle.label.color); @@ -43,6 +43,6 @@ export function updateNodeLabelStyle(nodeLabelGfx: Container, nodeStyle: NodeSty export function updateNodeLabelVisibility(nodeLabelGfx: Container, zoomStep: number) { // nodeLabelGfx -> nodeLabelText - const nodeLabelText = nodeLabelGfx.getChildByName!(NODE_LABEL_TEXT) as BitmapText; + const nodeLabelText = nodeLabelGfx.getChildByName!(NODE_LABEL_TEXT) as unknown as BitmapText; nodeLabelText.visible = zoomStep >= 3; } diff --git a/src/renderers/node.ts b/src/renderers/node.ts index 0a75aea..eb54305 100644 --- a/src/renderers/node.ts +++ b/src/renderers/node.ts @@ -61,18 +61,18 @@ export function updateNodeStyle(nodeGfx: Container, nodeStyle: NodeStyle, textur (nodeGfx.hitArea as Circle).radius = nodeOuterSize; // nodeGfx -> nodeCircle - const nodeCircle = nodeGfx.getChildByName!(NODE_CIRCLE) as Sprite; + const nodeCircle = nodeGfx.getChildByName!(NODE_CIRCLE) as unknown as Sprite; nodeCircle.texture = nodeCircleTexture; [nodeCircle.tint, nodeCircle.alpha] = colorToPixi(nodeStyle.color); // nodeGfx -> nodeCircleBorder - const nodeCircleBorder = nodeGfx.getChildByName!(NODE_CIRCLE_BORDER) as Sprite; + const nodeCircleBorder = nodeGfx.getChildByName!(NODE_CIRCLE_BORDER) as unknown as Sprite; nodeCircleBorder.texture = nodeCircleBorderTexture; [nodeCircleBorder.tint, nodeCircleBorder.alpha] = colorToPixi(nodeStyle.border.color); // nodeGfx -> nodeIcon if (nodeStyle.icon.url && nodeStyle.icon.width && nodeStyle.icon.height) { - const nodeIcon = nodeGfx.getChildByName!(NODE_ICON) as Sprite; + const nodeIcon = nodeGfx.getChildByName!(NODE_ICON) as unknown as Sprite; nodeIcon.texture = Texture.from(nodeStyle.icon.url); nodeIcon.width = nodeStyle.icon.width; nodeIcon.height = nodeStyle.icon.height; @@ -82,11 +82,11 @@ export function updateNodeStyle(nodeGfx: Container, nodeStyle: NodeStyle, textur export function updateNodeVisibility(nodeGfx: Container, zoomStep: number) { // nodeGfx -> nodeCircleBorder - const nodeCircleBorder = nodeGfx.getChildByName!(NODE_CIRCLE_BORDER) as Sprite; + const nodeCircleBorder = nodeGfx.getChildByName!(NODE_CIRCLE_BORDER) as unknown as Sprite; nodeCircleBorder.visible = zoomStep >= 1; // nodeGfx -> nodeIcon - const nodeIcon = nodeGfx.getChildByName!(NODE_ICON) as Sprite; + const nodeIcon = nodeGfx.getChildByName!(NODE_ICON) as unknown as Sprite; if (nodeIcon) { nodeIcon.visible = zoomStep >= 2; }