diff --git a/.vscode/settings.json b/.vscode/settings.json index 2814cb710b8..8e916bf5f91 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,9 +11,10 @@ "afterelementtranslate", "afterelementupdate", "afterlayout", - "afterstagelayout", "afterrender", + "afterrendererchange", "aftersizechange", + "afterstagelayout", "aftertransform", "afterviewportanimate", "antv", @@ -30,9 +31,10 @@ "beforeelementtranslate", "beforeelementupdate", "beforelayout", - "beforestagelayout", "beforerender", + "beforerendererchange", "beforesizechange", + "beforestagelayout", "beforetransform", "beforeviewportanimate", "bubblesets", diff --git a/packages/g6-extension-3d/__tests__/demos/index.ts b/packages/g6-extension-3d/__tests__/demos/index.ts index 211cb3449ab..46bc64f9e4b 100644 --- a/packages/g6-extension-3d/__tests__/demos/index.ts +++ b/packages/g6-extension-3d/__tests__/demos/index.ts @@ -8,3 +8,4 @@ export { massiveElements } from './massive-elements'; export * from './position'; export * from './shapes'; export * from './solar-system'; +export { switchRenderer } from './switch-renderer'; diff --git a/packages/g6-extension-3d/__tests__/demos/switch-renderer.ts b/packages/g6-extension-3d/__tests__/demos/switch-renderer.ts new file mode 100644 index 00000000000..884f45fda00 --- /dev/null +++ b/packages/g6-extension-3d/__tests__/demos/switch-renderer.ts @@ -0,0 +1,60 @@ +import { Renderer as CanvasRenderer } from '@antv/g-canvas'; +import type { NodeData } from '@antv/g6'; +import { ExtensionCategory, Graph, register } from '@antv/g6'; +import { Light, Sphere, renderer } from '../../src'; + +export const switchRenderer: TestCase = async (context) => { + register(ExtensionCategory.PLUGIN, '3d-light', Light); + register(ExtensionCategory.NODE, 'sphere', Sphere); + + const nodes: NodeData[] = [{ id: '1' }, { id: '2' }]; + + const graph = new Graph({ + ...context, + data: { + nodes, + }, + layout: { + type: 'grid', + }, + }); + + await graph.render(); + + switchRenderer.form = (panel) => { + panel.add({ renderer: '2d' }, 'renderer', ['2d', '3d']).onChange((name: string) => { + if (name === '2d') { + graph.setOptions({ + renderer: () => new CanvasRenderer(), + node: { + type: 'circle', + }, + plugins: [], + }); + } else { + graph.setOptions({ + renderer, + node: { + type: 'sphere', + style: { + materialType: 'phong', + }, + }, + plugins: [ + { + type: '3d-light', + directional: { + direction: [0, 0, 1], + }, + }, + ], + }); + } + + graph.draw(); + }); + return []; + }; + + return graph; +}; diff --git a/packages/g6-extension-3d/src/plugins/light.ts b/packages/g6-extension-3d/src/plugins/light.ts index 76351a0827f..f58b844e499 100644 --- a/packages/g6-extension-3d/src/plugins/light.ts +++ b/packages/g6-extension-3d/src/plugins/light.ts @@ -72,6 +72,8 @@ export class Light extends BasePlugin { } public destroy() { + this.ambient?.remove(); + this.directional?.remove(); this.unbindEvents(); super.destroy(); } diff --git a/packages/g6-extension-3d/src/utils/geometry.ts b/packages/g6-extension-3d/src/utils/geometry.ts index f6b60720956..27855f3a80d 100644 --- a/packages/g6-extension-3d/src/utils/geometry.ts +++ b/packages/g6-extension-3d/src/utils/geometry.ts @@ -1,6 +1,8 @@ import type { Device } from '@antv/g-device-api'; import type { ProceduralGeometry } from '@antv/g-plugin-3d'; +let DEVICE: Device; + const GEOMETRY_CACHE = new Map(); /** @@ -19,6 +21,12 @@ export function createGeometry>( Ctor: new (...args: any[]) => T, style: Record, ) { + if (!DEVICE) DEVICE = device; + else if (DEVICE !== device) { + DEVICE = device; + GEOMETRY_CACHE.clear(); + } + const cacheKey = type + '|' + diff --git a/packages/g6-extension-3d/src/utils/map.ts b/packages/g6-extension-3d/src/utils/map.ts index 14a10e3e09e..387938544f6 100644 --- a/packages/g6-extension-3d/src/utils/map.ts +++ b/packages/g6-extension-3d/src/utils/map.ts @@ -15,4 +15,8 @@ export class TupleMap { } this.map.get(key1)!.set(key2, value); } + + clear() { + this.map.clear(); + } } diff --git a/packages/g6-extension-3d/src/utils/material.ts b/packages/g6-extension-3d/src/utils/material.ts index 35d7a32388f..92d1cb9b573 100644 --- a/packages/g6-extension-3d/src/utils/material.ts +++ b/packages/g6-extension-3d/src/utils/material.ts @@ -6,6 +6,8 @@ import { getCacheKey } from './cache'; import { TupleMap } from './map'; import { createTexture } from './texture'; +let PLUGIN: Plugin; + const MATERIAL_CACHE = new TupleMap(); const MATERIAL_MAP = { @@ -25,6 +27,12 @@ const MATERIAL_MAP = { * @returns 材质对象 material object */ export function createMaterial(plugin: Plugin, options: Material, texture?: string | TexImageSource): GMaterial { + if (!PLUGIN) PLUGIN = plugin; + else if (PLUGIN !== plugin) { + PLUGIN = plugin; + MATERIAL_CACHE.clear(); + } + const key = getCacheKey(options); if (MATERIAL_CACHE.has(key, texture)) { diff --git a/packages/g6/__tests__/demos/canvas-switch-renderer.ts b/packages/g6/__tests__/demos/canvas-switch-renderer.ts new file mode 100644 index 00000000000..25cf13741d0 --- /dev/null +++ b/packages/g6/__tests__/demos/canvas-switch-renderer.ts @@ -0,0 +1,33 @@ +import { Graph } from '@/src'; +import { Renderer as CanvasRenderer } from '@antv/g-canvas'; +import { Renderer as SVGRenderer } from '@antv/g-svg'; +import { Renderer as WebGLRenderer } from '@antv/g-webgl'; + +export const canvasSwitchRenderer: TestCase = async (context) => { + const graph = new Graph({ + ...context, + data: { + nodes: Array.from({ length: 10 }).map((_, i) => ({ id: `node-${i}` })), + }, + layout: { + type: 'grid', + }, + }); + + await graph.render(); + + canvasSwitchRenderer.form = (panel) => { + panel.add({ renderer: 'canvas' }, 'renderer', ['canvas', 'svg', 'webgl']).onChange((name: string) => { + graph.setOptions({ + renderer: () => { + if (name === 'svg') return new SVGRenderer(); + if (name === 'webgl') return new WebGLRenderer(); + return new CanvasRenderer(); + }, + }); + }); + return []; + }; + + return graph; +}; diff --git a/packages/g6/__tests__/demos/index.ts b/packages/g6/__tests__/demos/index.ts index 586b124603f..13e723e1da2 100644 --- a/packages/g6/__tests__/demos/index.ts +++ b/packages/g6/__tests__/demos/index.ts @@ -19,6 +19,7 @@ export { behaviorLassoSelect } from './behavior-lasso-select'; export { behaviorOptimizeViewportTransform } from './behavior-optimize-viewport-transform'; export { behaviorScrollCanvas } from './behavior-scroll-canvas'; export { behaviorZoomCanvas } from './behavior-zoom-canvas'; +export { canvasSwitchRenderer } from './canvas-switch-renderer'; export { caseIndentedTree } from './case-indented-tree'; export { caseOrgChart } from './case-org-chart'; export { elementCombo } from './combo'; diff --git a/packages/g6/__tests__/unit/runtime/canvas.spec.ts b/packages/g6/__tests__/unit/runtime/canvas.spec.ts index a0f6a7835ab..fc6715e239a 100644 --- a/packages/g6/__tests__/unit/runtime/canvas.spec.ts +++ b/packages/g6/__tests__/unit/runtime/canvas.spec.ts @@ -1,4 +1,3 @@ -import { parsePoint } from '@/src/utils/point'; import { createGraphCanvas } from '@@/utils'; describe('Canvas', () => { @@ -19,32 +18,32 @@ describe('Canvas', () => { it('coordinate transform', () => { // TODO g canvas client 坐标转换疑似异常 - expect(parsePoint(svg.viewport2Client({ x: 0, y: 0 }))).toBeCloseTo([0, 0, 0]); - expect(parsePoint(svg.viewport2Canvas({ x: 0, y: 0 }))).toBeCloseTo([0, 0, 0]); + expect(svg.getClientByCanvas([0, 0])).toBeCloseTo([0, 0, 0]); + expect(svg.getCanvasByViewport([0, 0])).toBeCloseTo([0, 0, 0]); - expect(parsePoint(svg.client2Viewport({ x: 0, y: 0 }))).toBeCloseTo([0, 0, 0]); - expect(parsePoint(svg.canvas2Viewport({ x: 0, y: 0 }))).toBeCloseTo([0, 0, 0]); + expect(svg.getViewportByClient([0, 0])).toBeCloseTo([0, 0, 0]); + expect(svg.getViewportByCanvas([0, 0])).toBeCloseTo([0, 0, 0]); const camera = svg.getCamera(); camera.pan(100, 100); expect([...camera.getPosition()]).toBeCloseTo([350, 350, 500]); expect([...camera.getFocalPoint()]).toBeCloseTo([250, 250, 0]); - expect(parsePoint(svg.viewport2Canvas({ x: 0, y: 0 }))).toBeCloseTo([100, 100, 0]); - expect(parsePoint(svg.canvas2Viewport({ x: 0, y: 0 }))).toBeCloseTo([-100, -100, 0]); + expect(svg.getCanvasByViewport([0, 0])).toBeCloseTo([100, 100, 0]); + expect(svg.getViewportByCanvas([0, 0])).toBeCloseTo([-100, -100, 0]); // camera pan 采用相对移动 camera.pan(-200, -200); // focal point wont change // expect([...camera.getFocalPoint()]).toBeCloseTo([250, 250, 0]); expect([...camera.getPosition()]).toBeCloseTo([150, 150, 500]); - expect(parsePoint(svg.viewport2Canvas({ x: 0, y: 0 }))).toBeCloseTo([-100, -100, 0]); - expect(parsePoint(svg.canvas2Viewport({ x: 0, y: 0 }))).toBeCloseTo([100, 100, 0]); + expect(svg.getCanvasByViewport([0, 0])).toBeCloseTo([-100, -100, 0]); + expect(svg.getViewportByCanvas([0, 0])).toBeCloseTo([100, 100, 0]); // move to origin camera.pan(100, 100); camera.pan(-100, -100); - expect(parsePoint(svg.viewport2Canvas({ x: 0, y: 0 }))).toBeCloseTo([-100, -100, 0]); + expect(svg.getCanvasByViewport([0, 0])).toBeCloseTo([-100, -100, 0]); camera.pan(100, 100); }); @@ -69,8 +68,8 @@ describe('Canvas', () => { camera.gotoLandmark(landmark1, { onfinish: resolve }); }); - expect(parsePoint(svg.viewport2Canvas({ x: 0, y: 0 }))).toBeCloseTo([100, 100, 0]); - expect(parsePoint(svg.canvas2Viewport({ x: 0, y: 0 }))).toBeCloseTo([-100, -100, 0]); + expect(svg.getCanvasByViewport([0, 0])).toBeCloseTo([100, 100, 0]); + expect(svg.getViewportByCanvas([0, 0])).toBeCloseTo([-100, -100, 0]); const landmark2 = camera.createLandmark('landmark2', { // 视点坐标 / viewport coordinates @@ -83,8 +82,8 @@ describe('Canvas', () => { camera.gotoLandmark(landmark2, { onfinish: resolve }); }); - expect(parsePoint(svg.viewport2Canvas({ x: 0, y: 0 }))).toBeCloseTo([-100, -100, 0]); - expect(parsePoint(svg.canvas2Viewport({ x: 0, y: 0 }))).toBeCloseTo([100, 100, 0]); + expect(svg.getCanvasByViewport([0, 0])).toBeCloseTo([-100, -100, 0]); + expect(svg.getViewportByCanvas([0, 0])).toBeCloseTo([100, 100, 0]); expect([...camera.getFocalPoint()]).toBeCloseTo([150, 150, 0]); expect([...camera.getPosition()]).toBeCloseTo([150, 150, 500]); diff --git a/packages/g6/__tests__/unit/runtime/graph/event.spec.ts b/packages/g6/__tests__/unit/runtime/graph/event.spec.ts index 102840101fa..91321bbb5f8 100644 --- a/packages/g6/__tests__/unit/runtime/graph/event.spec.ts +++ b/packages/g6/__tests__/unit/runtime/graph/event.spec.ts @@ -1,5 +1,6 @@ import { GraphEvent } from '@/src'; import { createGraph } from '@@/utils'; +import { Renderer as CanvasRenderer } from '@antv/g-canvas'; describe('event', () => { it('canvas ready', async () => { @@ -153,4 +154,33 @@ describe('event', () => { graph.destroy(); }); + + it('renderer change event', async () => { + const graph = createGraph({ + data: { + nodes: [{ id: 'node-1' }, { id: 'node-2' }], + edges: [{ id: 'edge-1', source: 'node-1', target: 'node-2' }], + }, + }); + + const beforeRendererChange = jest.fn(); + const afterRendererChange = jest.fn(); + + graph.on(GraphEvent.BEFORE_RENDERER_CHANGE, beforeRendererChange); + graph.on(GraphEvent.AFTER_RENDERER_CHANGE, afterRendererChange); + + await graph.render(); + + expect(beforeRendererChange).toHaveBeenCalledTimes(0); + expect(afterRendererChange).toHaveBeenCalledTimes(0); + + const renderer = () => new CanvasRenderer(); + + graph.setOptions({ + renderer, + }); + + expect(beforeRendererChange).toHaveBeenCalledTimes(1); + expect(afterRendererChange).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/g6/__tests__/utils/create.ts b/packages/g6/__tests__/utils/create.ts index 71a2a26bb71..9235bae63a8 100644 --- a/packages/g6/__tests__/utils/create.ts +++ b/packages/g6/__tests__/utils/create.ts @@ -6,6 +6,7 @@ import type { Node, Point } from '@/src/types'; import { resetEntityCounter } from '@antv/g'; import { Renderer as CanvasRenderer } from '@antv/g-canvas'; import { Renderer as SVGRenderer } from '@antv/g-svg'; +import { Renderer as WebGLRenderer } from '@antv/g-webgl'; import { OffscreenCanvasContext } from './offscreen-canvas-context'; function getRenderer(renderer: string) { @@ -13,6 +14,7 @@ function getRenderer(renderer: string) { case 'svg': return new SVGRenderer(); case 'webgl': + return new WebGLRenderer(); case 'canvas': return new CanvasRenderer(); default: diff --git a/packages/g6/package.json b/packages/g6/package.json index c9653c9667d..2b29b992837 100644 --- a/packages/g6/package.json +++ b/packages/g6/package.json @@ -71,6 +71,7 @@ }, "devDependencies": { "@antv/g-svg": "^2.0.8", + "@antv/g-webgl": "^2.0.11", "@antv/layout-gpu": "^1.1.6", "@antv/layout-wasm": "^1.4.1", "@types/hull.js": "^1.0.4", diff --git a/packages/g6/src/constants/events/graph.ts b/packages/g6/src/constants/events/graph.ts index ee385f0b96e..5ee88ae4cb9 100644 --- a/packages/g6/src/constants/events/graph.ts +++ b/packages/g6/src/constants/events/graph.ts @@ -167,4 +167,16 @@ export enum GraphEvent { * After destruction */ AFTER_DESTROY = 'afterdestroy', + /** + * 渲染器变更之前 + * + * Before the renderer changes + */ + BEFORE_RENDERER_CHANGE = 'beforerendererchange', + /** + * 渲染器变更之后 + * + * After the renderer changes + */ + AFTER_RENDERER_CHANGE = 'afterrendererchange', } diff --git a/packages/g6/src/elements/nodes/html.ts b/packages/g6/src/elements/nodes/html.ts index dabf5bad8ec..20f009c6090 100644 --- a/packages/g6/src/elements/nodes/html.ts +++ b/packages/g6/src/elements/nodes/html.ts @@ -215,7 +215,7 @@ export class HTML extends BaseNode { const { x, y } = this.getViewportXY(normalizedEvent); event.viewport.x = x; event.viewport.y = y; - const { x: canvasX, y: canvasY } = this.attributes.context!.canvas.viewport2Canvas(event.viewport); + const [canvasX, canvasY] = this.context.canvas.getCanvasByViewport([x, y]); event.canvas.x = canvasX; event.canvas.y = canvasY; event.global.copyFrom(event.canvas); diff --git a/packages/g6/src/runtime/canvas.ts b/packages/g6/src/runtime/canvas.ts index d818b2aa1b5..5717ebeddd1 100644 --- a/packages/g6/src/runtime/canvas.ts +++ b/packages/g6/src/runtime/canvas.ts @@ -1,11 +1,12 @@ -import type { DisplayObject, CanvasConfig as GCanvasConfig, IChildNode } from '@antv/g'; +import type { Cursor, DisplayObject, CanvasConfig as GCanvasConfig, IChildNode } from '@antv/g'; import { CanvasEvent, Canvas as GCanvas } from '@antv/g'; import { Renderer as CanvasRenderer } from '@antv/g-canvas'; import { Plugin as DragNDropPlugin } from '@antv/g-plugin-dragndrop'; import { createDOM } from '@antv/util'; import type { CanvasOptions } from '../spec/canvas'; -import type { CanvasLayer } from '../types'; +import type { CanvasLayer, Point } from '../types'; import { getBBoxSize, getCombinedBBox } from '../utils/bbox'; +import { parsePoint, toPointObject } from '../utils/point'; export interface CanvasConfig extends Pick { @@ -40,7 +41,7 @@ export interface DataURLOptions { const layersName: CanvasLayer[] = ['background', 'main', 'label', 'transient']; -export class Canvas extends GCanvas { +export class Canvas { private extends: { config: CanvasConfig; renderer: CanvasOptions['renderer']; @@ -48,7 +49,13 @@ export class Canvas extends GCanvas { layers: Record; }; - public getLayer(layer: CanvasLayer) { + private config: CanvasConfig; + + public getConfig() { + return this.config; + } + + public getLayer(layer: CanvasLayer = 'main') { return this.extends.layers[layer]; } @@ -73,17 +80,46 @@ export class Canvas extends GCanvas { return this.extends.renderers[layer]; } + /** + * 获取相机 + * + * Get camera + * @param layer - 图层 Layer + * @returns 相机 Camera + */ + public getCamera(layer: CanvasLayer = 'main') { + return this.getLayer(layer).getCamera(); + } + + public getRoot(layer: CanvasLayer = 'main') { + return this.getLayer(layer).getRoot(); + } + + public getContextService(layer: CanvasLayer = 'main') { + return this.getLayer(layer).getContextService(); + } + + public setCursor(cursor: Cursor): void { + this.config.cursor = cursor; + this.getLayer().setCursor(cursor); + } + + public get document() { + return this.getLayer().document; + } + + public get context() { + return this.getLayer().context; + } + constructor(config: CanvasConfig) { + this.config = config; + const { renderer, background, cursor, ...restConfig } = config; const renderers = createRenderers(renderer); - // init main canvas - super({ ...restConfig, supportsMutipleCanvasesInOneContainer: true, cursor, renderer: renderers.main }); - const layers = Object.fromEntries( layersName.map((layer) => { - if (layer === 'main') return [layer, this]; - const canvas = new GCanvas({ ...restConfig, supportsMutipleCanvasesInOneContainer: true, @@ -106,20 +142,12 @@ export class Canvas extends GCanvas { } public get ready() { - return Promise.all([ - super.ready, - ...Object.entries(this.getLayers()).map(([layer, canvas]) => - layer === 'main' ? Promise.resolve() : canvas.ready, - ), - ]); + return Promise.all(Object.entries(this.getLayers()).map(([, canvas]) => canvas.ready)); } public resize(width: number, height: number) { Object.assign(this.extends.config, { width, height }); - Object.values(this.getLayers()).forEach((canvas) => { - if (canvas === this) super.resize(width, height); - else canvas.resize(width, height); - }); + Object.values(this.getLayers()).forEach((canvas) => canvas.resize(width, height)); } public getBounds() { @@ -142,19 +170,41 @@ export class Canvas extends GCanvas { public appendChild(child: T, index?: number): T { const layer = ((child as unknown as DisplayObject).style?.$layer || 'main') as CanvasLayer; - if (layer === 'main') return super.appendChild(child, index); return this.getLayer(layer).appendChild(child, index); } - public setRenderer(renderer: any) { + public setRenderer(renderer: CanvasOptions['renderer']) { if (renderer === this.extends.renderer) return; const renderers = createRenderers(renderer); this.extends.renderers = renderers; + Object.entries(renderers).forEach(([layer, instance]) => this.getLayer(layer as CanvasLayer).setRenderer(instance)); + configCanvasDom(this.getLayers()); + } - Object.entries(renderers).forEach(([layer, instance]) => { - if (layer === 'main') super.setRenderer(instance); - else this.getLayer(layer as CanvasLayer).setRenderer(instance); - }); + public getCanvasByViewport(point: Point): Point { + return parsePoint(this.getLayer().viewport2Canvas(toPointObject(point))); + } + + public getViewportByCanvas(point: Point): Point { + return parsePoint(this.getLayer().canvas2Viewport(toPointObject(point))); + } + + public getViewportByClient(point: Point): Point { + return parsePoint(this.getLayer().client2Viewport(toPointObject(point))); + } + + public getClientByViewport(point: Point): Point { + return parsePoint(this.getLayer().viewport2Client(toPointObject(point))); + } + + public getClientByCanvas(point: Point): Point { + return this.getClientByViewport(this.getViewportByCanvas(point)); + } + + public getCanvasByClient(point: Point): Point { + const main = this.getLayer(); + const viewportPoint = main.client2Viewport(toPointObject(point)); + return parsePoint(main.viewport2Canvas(viewportPoint)); } public async toDataURL(options: Partial = {}) { @@ -190,10 +240,10 @@ export class Canvas extends GCanvas { // Handle label canvas const label = this.getLayer('label').getRoot().cloneNode(true); const originCanvasPosition = offscreenCanvas.viewport2Canvas({ x: 0, y: 0 }); - const currentCanvasPosition = this.viewport2Canvas({ x: 0, y: 0 }); + const currentCanvasPosition = this.getCanvasByViewport([0, 0]); label.translate([ - currentCanvasPosition.x - originCanvasPosition.x, - currentCanvasPosition.y - originCanvasPosition.y, + currentCanvasPosition[0] - originCanvasPosition.x, + currentCanvasPosition[1] - originCanvasPosition.y, ]); label.scale(1 / this.getCamera().getZoom()); offscreenCanvas.appendChild(label); @@ -226,12 +276,11 @@ export class Canvas extends GCanvas { }); } - public destroy(cleanUp?: boolean, skipTriggerEvent?: boolean) { + public destroy() { Object.values(this.getLayers()).forEach((canvas) => { const camera = canvas.getCamera(); camera.cancelLandmarkAnimation(); - if (canvas === this) super.destroy(cleanUp, skipTriggerEvent); - else canvas.destroy(cleanUp, skipTriggerEvent); + canvas.destroy(); }); } } diff --git a/packages/g6/src/runtime/element.ts b/packages/g6/src/runtime/element.ts index b2cae7818ce..7dbc06e1d0a 100644 --- a/packages/g6/src/runtime/element.ts +++ b/packages/g6/src/runtime/element.ts @@ -451,6 +451,9 @@ export class ElementController { }, { before: () => { + // 通过 elementMap[id] 访问最新的 element,防止 type 不同导致的 element 丢失 + // Access the latest element through elementMap[id] to prevent the loss of element caused by different types + const element = this.elementMap[id]; if (stage !== 'collapse') updateStyle(element, style); if (stage === 'visibility') { @@ -462,6 +465,7 @@ export class ElementController { } }, after: () => { + const element = this.elementMap[id]; if (stage === 'collapse') updateStyle(element, style); if (exactStage === 'hide') updateStyle(element, { visibility: getCachedStyle(element, 'visibility') }); this.emit(new ElementLifeCycleEvent(GraphEvent.AFTER_ELEMENT_UPDATE, elementType, datum), context); diff --git a/packages/g6/src/runtime/graph.ts b/packages/g6/src/runtime/graph.ts index 8bb63df4125..884174f2692 100644 --- a/packages/g6/src/runtime/graph.ts +++ b/packages/g6/src/runtime/graph.ts @@ -43,7 +43,6 @@ import { isCollapsed } from '../utils/collapsibility'; import { sizeOf } from '../utils/dom'; import { GraphLifeCycleEvent, emit } from '../utils/event'; import { idOf } from '../utils/id'; -import { parsePoint, toPointObject } from '../utils/point'; import { format } from '../utils/print'; import { subtract } from '../utils/vector'; import { Animation } from './animation'; @@ -120,6 +119,15 @@ export class Graph extends EventEmitter { public setOptions(options: GraphOptions): void { const { behaviors, combo, data, edge, height, layout, node, plugins, theme, transforms, width, renderer } = options; + if (renderer) { + const canvas = this.context.canvas; + if (canvas) { + this.emit(GraphEvent.BEFORE_RENDERER_CHANGE, { renderer: this.options.renderer }); + canvas.setRenderer(renderer); + this.emit(GraphEvent.AFTER_RENDERER_CHANGE, { renderer }); + } + } + Object.assign(this.options, options); if (behaviors) this.setBehaviors(behaviors); @@ -133,7 +141,6 @@ export class Graph extends EventEmitter { if (transforms) this.setTransforms(transforms); if (isNumber(width) || isNumber(height)) this.setSize(width ?? this.options.width ?? 0, height ?? this.options.height ?? 0); - if (renderer) this.context.canvas?.setRenderer(renderer); } /** @@ -1785,7 +1792,7 @@ export class Graph extends EventEmitter { * @apiCategory viewport */ public getCanvasByViewport(point: Point): Point { - return parsePoint(this.context.canvas!.viewport2Canvas(toPointObject(point))); + return this.context.canvas.getCanvasByViewport(point); } /** @@ -1797,7 +1804,7 @@ export class Graph extends EventEmitter { * @apiCategory viewport */ public getViewportByCanvas(point: Point): Point { - return parsePoint(this.context.canvas.canvas2Viewport(toPointObject(point))); + return this.context.canvas.getViewportByCanvas(point); } /** @@ -1809,8 +1816,7 @@ export class Graph extends EventEmitter { * @apiCategory viewport */ public getClientByCanvas(point: Point): Point { - const viewportPoint = this.context.canvas.canvas2Viewport(toPointObject(point)); - return parsePoint(this.context.canvas.viewport2Canvas(viewportPoint)); + return this.context.canvas.getClientByCanvas(point); } /** @@ -1822,8 +1828,7 @@ export class Graph extends EventEmitter { * @apiCategory viewport */ public getCanvasByClient(point: Point): Point { - const viewportPoint = this.context.canvas.client2Viewport(toPointObject(point)); - return parsePoint(this.context.canvas.viewport2Canvas(viewportPoint)); + return this.context.canvas.getCanvasByClient(point); } /**