From 8719c3a0c33ff5b5f7c245c57060aeba113b220a Mon Sep 17 00:00:00 2001 From: Mikey Gower Date: Tue, 30 Jan 2024 14:21:47 -0500 Subject: [PATCH 1/4] fix vulnerabilities --- package-lock.json | 12 ++++++------ src/webgl/index.ts | 2 +- .../{interaction => interactions}/decelerate.ts | 0 src/webgl/{interaction => interactions}/drag.ts | 0 src/webgl/{interaction => interactions}/index.ts | 0 src/webgl/{interaction => interactions}/zoom.ts | 0 6 files changed, 7 insertions(+), 7 deletions(-) rename src/webgl/{interaction => interactions}/decelerate.ts (100%) rename src/webgl/{interaction => interactions}/drag.ts (100%) rename src/webgl/{interaction => interactions}/index.ts (100%) rename src/webgl/{interaction => interactions}/zoom.ts (100%) diff --git a/package-lock.json b/package-lock.json index 286518b0..af471171 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6397,9 +6397,9 @@ "dev": true }, "node_modules/msgpackr": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.9.9.tgz", - "integrity": "sha512-sbn6mioS2w0lq1O6PpGtsv6Gy8roWM+o3o4Sqjd6DudrL/nOugY+KyJUimoWzHnf9OkO0T6broHFnYE/R05t9A==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.1.tgz", + "integrity": "sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==", "dev": true, "optionalDependencies": { "msgpackr-extract": "^3.0.2" @@ -7760,9 +7760,9 @@ } }, "node_modules/vite": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", - "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", "dev": true, "dependencies": { "esbuild": "^0.18.10", diff --git a/src/webgl/index.ts b/src/webgl/index.ts index 81fe14a3..8b86932e 100644 --- a/src/webgl/index.ts +++ b/src/webgl/index.ts @@ -3,7 +3,7 @@ import { ArrowTexture, CircleTexture, FontBook, TextIconTexture, ImageIconTextur import { NodeRenderer, EdgeRenderer, ObjectManager } from './objects' import { interpolate, logUnknownEdgeError } from '../utils' import { Viewport, Node, Edge, Annotation } from '../types' -import { Zoom, Drag, Decelerate } from './interaction' +import { Zoom, Drag, Decelerate } from './interactions' import { Grid } from './grid' import Stats from 'stats.js' diff --git a/src/webgl/interaction/decelerate.ts b/src/webgl/interactions/decelerate.ts similarity index 100% rename from src/webgl/interaction/decelerate.ts rename to src/webgl/interactions/decelerate.ts diff --git a/src/webgl/interaction/drag.ts b/src/webgl/interactions/drag.ts similarity index 100% rename from src/webgl/interaction/drag.ts rename to src/webgl/interactions/drag.ts diff --git a/src/webgl/interaction/index.ts b/src/webgl/interactions/index.ts similarity index 100% rename from src/webgl/interaction/index.ts rename to src/webgl/interactions/index.ts diff --git a/src/webgl/interaction/zoom.ts b/src/webgl/interactions/zoom.ts similarity index 100% rename from src/webgl/interaction/zoom.ts rename to src/webgl/interactions/zoom.ts From 35b5194fda15030c0d60da18d32253c3f3c5a279 Mon Sep 17 00:00:00 2001 From: Mikey Gower Date: Tue, 30 Jan 2024 16:00:23 -0500 Subject: [PATCH 2/4] RendererOptions class to manage app options --- src/types/api/events.ts | 101 ++++++++++++++++ src/types/api/index.ts | 3 + src/types/api/objects.ts | 199 ++++++++++++++++++++++++++++++++ src/types/api/options.ts | 83 +++++++++++++ src/types/{api.ts => api_v1.ts} | 0 src/types/index.ts | 2 +- src/types/internal.ts | 21 ++++ src/utils/constants.ts | 18 +++ src/utils/helpers.ts | 3 + src/webgl/RendererOptions.ts | 48 ++++++++ 10 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 src/types/api/events.ts create mode 100644 src/types/api/index.ts create mode 100644 src/types/api/objects.ts create mode 100644 src/types/api/options.ts rename src/types/{api.ts => api_v1.ts} (100%) create mode 100644 src/webgl/RendererOptions.ts diff --git a/src/types/api/events.ts b/src/types/api/events.ts new file mode 100644 index 00000000..368245e0 --- /dev/null +++ b/src/types/api/events.ts @@ -0,0 +1,101 @@ +import { Node, Edge, Annotation, Viewport } from './objects' + +export type RectPoint = 'nw' | 'ne' | 'se' | 'sw' + +interface EventBase { + type: T + altKey?: boolean + ctrlKey?: boolean + metaKey?: boolean + shiftKey?: boolean +} + +interface MouseEvent extends EventBase { + x: number + y: number + clientX: number + clientY: number +} + +interface DragEvent extends MouseEvent { + dx: number + dy: number +} + +export interface NodePointerEvent extends MouseEvent<'nodePointer'> { + target: Node + targetIndex: number +} + +export interface NodeDragEvent extends DragEvent<'nodeDrag'> { + target: Node + targetIndex: number +} + +export interface EdgePointerEvent extends MouseEvent<'edgePointer'> { + target: Edge + targetIndex: number +} + +export interface AnnotationPointerEvent extends MouseEvent<'annotationPointer'> { + target: Annotation + targetIndex: number + relatedPoint: RectPoint | null +} + +export interface AnnotationDragEvent extends DragEvent<'annotationDrag'> { + target: Annotation + targetIndex: number +} + +export interface AnnotationResizeEvent extends MouseEvent<'annotationResize'> { + target: Annotation + targetIndex: number + relatedPoint: RectPoint +} + +export interface ViewportPointerEvent extends MouseEvent<'viewportPointer'> { + target: Viewport +} + +export interface ViewportDragEvent extends DragEvent<'viewportDrag'> { + target: Viewport +} + +export interface ViewportDragDecelerateEvent extends EventBase<'viewportDragDecelerate'> { + dx: number + dy: number +} + +export interface ViewportWheelEvent extends DragEvent<'viewportWheel'> { + dz: number +} + +export interface EventHandlers { + onViewportPointerEnter?: (event: ViewportPointerEvent) => void + onViewportPointerDown?: (event: ViewportPointerEvent) => void + onViewportPointerMove?: (event: ViewportPointerEvent) => void + onViewportDragStart?: (event: ViewportDragEvent | ViewportDragDecelerateEvent) => void + onViewportDrag?: (event: ViewportDragEvent | ViewportDragDecelerateEvent) => void + onViewportDragEnd?: (event: ViewportDragEvent | ViewportDragDecelerateEvent) => void + onViewportPointerUp?: (event: ViewportPointerEvent) => void + onViewportClick?: (event: ViewportPointerEvent) => void + onViewportDoubleClick?: (event: ViewportPointerEvent) => void + onViewportPointerLeave?: (event: ViewportPointerEvent) => void + onViewportWheel?: (event: ViewportWheelEvent) => void + onNodePointerEnter?: (event: NodePointerEvent) => void + onNodePointerDown?: (event: NodePointerEvent) => void + onNodeDragStart?: (event: NodeDragEvent) => void + onNodeDrag?: (event: NodeDragEvent) => void + onNodeDragEnd?: (event: NodeDragEvent) => void + onNodePointerUp?: (event: NodePointerEvent) => void + onNodeClick?: (event: NodePointerEvent) => void + onNodeDoubleClick?: (event: NodePointerEvent) => void + onNodePointerLeave?: (event: NodePointerEvent) => void + onEdgePointerEnter?: (event: EdgePointerEvent) => void + onEdgePointerDown?: (event: EdgePointerEvent) => void + onEdgePointerUp?: (event: EdgePointerEvent) => void + onEdgePointerLeave?: (event: EdgePointerEvent) => void + onEdgeClick?: (event: EdgePointerEvent) => void + onEdgeDoubleClick?: (event: EdgePointerEvent) => void +} diff --git a/src/types/api/index.ts b/src/types/api/index.ts new file mode 100644 index 00000000..95256fb1 --- /dev/null +++ b/src/types/api/index.ts @@ -0,0 +1,3 @@ +export * from './objects' +export * from './events' +export * from './options' diff --git a/src/types/api/objects.ts b/src/types/api/objects.ts new file mode 100644 index 00000000..b01175b9 --- /dev/null +++ b/src/types/api/objects.ts @@ -0,0 +1,199 @@ +// viewport +export interface Bounds { + left: number + top: number + right: number + bottom: number +} + +export interface Dimensions { + width: number + height: number +} + +export interface Coordinates { + x: number + y: number +} + +export interface Viewport extends Coordinates { + zoom: number +} + +export interface Graph { + nodes: Node[] + edges: Edge[] +} + +// style +export interface Stroke { + color: string + width: number +} + +export interface FillStyle { + color: string + opacity?: number +} + +// text +export type FontWeight = 'normal' | 'bold' | 'bolder' | 'lighter' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' + +export type TextAlign = 'left' | 'center' | 'right' | 'justify' + +export type FontStyle = 'normal' | 'italic' | 'oblique' + +export type AnchorPosition = 'bottom' | 'left' | 'top' | 'right' + +export interface TextHighlightStyle extends FillStyle { + padding?: number | number[] +} + +export interface TextStyle { + fontName?: string + fontSize?: number + margin?: number + wordWrap?: number + letterSpacing?: number + fontFamily?: string + fontWeight?: FontWeight + fontStyle?: FontStyle + stroke?: Stroke + color?: string + align?: TextAlign + anchor?: AnchorPosition + highlight?: TextHighlightStyle +} + +// icons +interface IconBase { + type: Type + offset?: Partial +} + +export interface TextIcon extends IconBase<'textIcon'> { + content: string + color: string + /** + * @default 10 + */ + fontSize?: number + /** + * @default 'sans-serif' + */ + fontFamily?: string + /** + * @default 'normal' + */ + fontStyle?: FontStyle + /** + * @default 'normal' + */ + fontWeight?: FontWeight +} + +export interface ImageIcon extends IconBase<'imageIcon'> { + url: string + /** + * @default 1 + */ + scale?: number +} + +export type IconStyle = TextIcon | ImageIcon + +// nodes +export interface NodeBadge { + position: number + radius: number + color: string + icon?: IconStyle + stroke?: Stroke +} + +export interface NodeStyle { + color?: string + icon?: IconStyle + stroke?: Stroke[] + badge?: NodeBadge[] + label?: TextStyle +} + +export interface Node { + id: string + radius: number + x?: number + y?: number + fx?: number + fy?: number + label?: string + style?: NodeStyle + subgraph?: Graph +} + +// edges +export type ArrowStyle = 'forward' | 'reverse' | 'both' | 'none' + +export interface EdgeLabelStyle extends TextStyle { + anchor?: Exclude +} + +export interface EdgeStyle { + /** + * @default 1 + */ + opacity?: number + /** + * @default 'none' + */ + arrow?: ArrowStyle + label?: EdgeLabelStyle + stroke?: Stroke +} + +export interface Edge { + id: string + source: string + target: string + label?: string + style?: EdgeStyle +} + +// annotations +export interface AnnotationStyle { + background?: FillStyle + stroke?: Stroke[] +} + +export interface TextAnnotationStyle extends AnnotationStyle { + text?: Omit + padding?: number | number[] +} + +interface AnnotationType { + type: Type + id: string + x: number + y: number + resize?: boolean +} + +export interface CircleAnnotation extends AnnotationType<'circle'> { + radius: number + style: AnnotationStyle +} + +export interface RectangleAnnotation extends AnnotationType<'rectangle'> { + height: number + width: number + style: AnnotationStyle +} + +export interface TextAnnotation extends AnnotationType<'text'> { + content: string + height: number + width: number + style: TextAnnotationStyle +} + +export type Annotation = CircleAnnotation | RectangleAnnotation | TextAnnotation diff --git a/src/types/api/options.ts b/src/types/api/options.ts new file mode 100644 index 00000000..a211455b --- /dev/null +++ b/src/types/api/options.ts @@ -0,0 +1,83 @@ +import { Viewport } from '.' +import { Concrete } from '../internal' + +// renderer options +export interface Options { + /** + * canvas resolution + * @default 2 + */ + resolution?: number + /** + * the minimum zoom on canvas + * @default 0.025 + */ + minZoom?: number + /** + * the maximum zoom on canvas + * @default 3 + */ + maxZoom?: number + /** + * the minimum zoom to show labels + * @default 0.25 + */ + minLabelZoom?: number + /** + * the minimum zoom to enable interactions + * @default 0.15 + */ + minInteractionZoom?: number + /** + * the minimum zoom to show edges + * @default 0.1 + */ + minEdgeZoom?: number + /** + * the minimum zoom to show decorations such as strokes, icons, etc. + * @default 0.3 + */ + minDecorationZoom?: number + /** + * the minimum zoom to render pixi textures + * @default 3 + */ + minTextureZoom?: number + /** + * the maximum node radius + * @default 15 + */ + maxRadius?: number + /** + * disable viewport animation or apply a duration in milliseconds + * @default 800 + */ + animateViewport?: number | boolean + /** + * disable node position animation or apply a duration in milliseconds + * @default 2000 + */ + animateNodePosition?: number | boolean + /** + * disable node radius animation or apply a duration in milliseconds + * @default 800 + */ + animateNodeRadius?: number | boolean + /** + * modify the drag inertia for deceleration interactions + * @default 0.88 + */ + dragInertia?: number + /** + * size of edge arrows + * @default { height: 12, width: 6 } + */ + arrowSize?: { height: number; width: number } +} + +export interface DefaultOptions extends Concrete { + defaultViewport: Viewport + animateViewport: number | false + animateNodePosition: number | false + animateNodeRadius: number | false +} diff --git a/src/types/api.ts b/src/types/api_v1.ts similarity index 100% rename from src/types/api.ts rename to src/types/api_v1.ts diff --git a/src/types/index.ts b/src/types/index.ts index b01c65ab..d8b4a66a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,2 +1,2 @@ -export * from './api' +export * from './api_v1' export * from './internal' diff --git a/src/types/internal.ts b/src/types/internal.ts index 376f1213..7c249b08 100644 --- a/src/types/internal.ts +++ b/src/types/internal.ts @@ -4,6 +4,10 @@ export type Extend = { [K in Exclude]: T[K] } & R +export type Concrete = { + [Property in keyof Type]-?: Type[Property] +} + export interface RenderObject { mounted: boolean // moveTo(...args: number[]): this @@ -18,3 +22,20 @@ export interface Interactions { pointerUp(event: FederatedPointerEvent): void pointerLeave(event: FederatedPointerEvent): void } + +export interface RenderObjectLifecycle { + mounted: boolean + mount(index?: number): this + unmount(): this + delete(): void +} + +export interface RendererClass { + update(...args: unknown[]): this + render(dt: number): this + delete(): void +} + +export interface InterpolateFunction { + (dt: number): { value: number; done: boolean } +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 5598575f..9bb4398c 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -13,3 +13,21 @@ export const MIN_NODE_ICON_ZOOM = 0.3 export const MIN_INTERACTION_ZOOM = 0.15 export const MIN_EDGES_ZOOM = 0.1 export const MIN_ZOOM = 3 + +export const DEFAULT_OPTIONS = { + maxZoom: 3, + minZoom: 0.025, + minEdgeZoom: 0.1, + minLabelZoom: 0.25, + minInteractionZoom: 0.15, + minTextureZoom: 3, + minDecorationZoom: 0.3, + animateViewport: 800, + animateNodePosition: 2000, + animateNodeRadius: 800, + dragInertia: 0.88, + maxRadius: 15, + resolution: 2, + arrowSize: { width: 6, height: 12 }, + defaultViewport: { x: 0, y: 0, zoom: 1 } +} diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 42d41339..5a6d90c2 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,5 +1,8 @@ import { interpolateBasis, interpolateNumber } from 'd3-interpolate' +export const isNumber = (value: unknown): value is number => typeof value === 'number' +export const isString = (value: unknown): value is string => typeof value === 'string' + export const noop = () => {} export const throttle = (cb: (...args: T) => void, duration: number) => { diff --git a/src/webgl/RendererOptions.ts b/src/webgl/RendererOptions.ts new file mode 100644 index 00000000..142b896a --- /dev/null +++ b/src/webgl/RendererOptions.ts @@ -0,0 +1,48 @@ +import { Options, DefaultOptions, Viewport, Dimensions } from '../types/api' +import { DEFAULT_OPTIONS, isNumber } from 'src/utils' + +export default class RendererOptions implements DefaultOptions { + private _defaultOptions: DefaultOptions + + resolution: number = DEFAULT_OPTIONS.resolution + defaultViewport: Viewport = DEFAULT_OPTIONS.defaultViewport + minZoom: number = DEFAULT_OPTIONS.minZoom + maxZoom: number = DEFAULT_OPTIONS.maxZoom + minEdgeZoom: number = DEFAULT_OPTIONS.minEdgeZoom + minLabelZoom: number = DEFAULT_OPTIONS.minLabelZoom + minInteractionZoom: number = DEFAULT_OPTIONS.minInteractionZoom + minTextureZoom: number = DEFAULT_OPTIONS.minTextureZoom + minDecorationZoom: number = DEFAULT_OPTIONS.minDecorationZoom + animateViewport: number | false = DEFAULT_OPTIONS.animateViewport + animateNodePosition: number | false = DEFAULT_OPTIONS.animateNodePosition + animateNodeRadius: number | false = DEFAULT_OPTIONS.animateNodeRadius + dragInertia: number = DEFAULT_OPTIONS.dragInertia + maxRadius: number = DEFAULT_OPTIONS.maxRadius + arrowSize: Dimensions = DEFAULT_OPTIONS.arrowSize + + constructor(defaultOptions: DefaultOptions) { + this._defaultOptions = defaultOptions + this.set({}) + } + + set({ animateViewport = true, animateNodeRadius = true, animateNodePosition = true, ...options }: Options): this { + Object.assign(this, options, this._defaultOptions, { + animateViewport: this.coerceAnimationValue('animateViewport', animateViewport), + animateNodeRadius: this.coerceAnimationValue('animateNodeRadius', animateNodeRadius), + animateNodePosition: this.coerceAnimationValue('animateNodePosition', animateNodePosition) + }) + + return this + } + + private coerceAnimationValue( + key: 'animateViewport' | 'animateNodePosition' | 'animateNodeRadius', + value: number | boolean + ): number | false { + return value === false || isNumber(value) + ? value + : isNumber(this._defaultOptions[key]) + ? this._defaultOptions[key] + : DEFAULT_OPTIONS[key] + } +} From 1f22a57b8e23f2e0ccb6606a1e4531b69b478fe0 Mon Sep 17 00:00:00 2001 From: Mikey Gower Date: Tue, 30 Jan 2024 16:49:06 -0500 Subject: [PATCH 3/4] InteractionManager, RenderObject, and HitArea base classes --- src/types/api/options.ts | 5 + src/utils/constants.ts | 50 +++++++++- src/webgl/RenderObject.ts | 72 ++++++++++++++ src/webgl/RendererOptions.ts | 11 ++- src/webgl/interactions/ApplicationEvents.ts | 97 ++++++++++++++++++ src/webgl/interactions/Interaction.ts | 21 ++++ src/webgl/interactions/InteractionManager.ts | 34 +++++++ src/webgl/interactions/events/EventManager.ts | 41 ++++++++ .../interactions/hitArea/CircleHitArea.ts | 32 ++++++ src/webgl/interactions/hitArea/HitArea.ts | 36 +++++++ src/webgl/interactions/hitArea/LineHitArea.ts | 28 ++++++ .../interactions/hitArea/RectangleHitArea.ts | 51 ++++++++++ src/webgl/interactions/index.ts | 6 +- src/webgl/interactions/plugins/Decelerate.ts | 98 +++++++++++++++++++ src/webgl/interactions/plugins/Drag.ts | 90 +++++++++++++++++ .../interactions/plugins/InteractionPlugin.ts | 27 +++++ src/webgl/interactions/plugins/Zoom.ts | 82 ++++++++++++++++ src/webgl/interactions/{ => v1}/decelerate.ts | 2 +- src/webgl/interactions/{ => v1}/drag.ts | 2 +- src/webgl/interactions/{ => v1}/zoom.ts | 2 +- 20 files changed, 775 insertions(+), 12 deletions(-) create mode 100644 src/webgl/RenderObject.ts create mode 100644 src/webgl/interactions/ApplicationEvents.ts create mode 100644 src/webgl/interactions/Interaction.ts create mode 100644 src/webgl/interactions/InteractionManager.ts create mode 100644 src/webgl/interactions/events/EventManager.ts create mode 100644 src/webgl/interactions/hitArea/CircleHitArea.ts create mode 100644 src/webgl/interactions/hitArea/HitArea.ts create mode 100644 src/webgl/interactions/hitArea/LineHitArea.ts create mode 100644 src/webgl/interactions/hitArea/RectangleHitArea.ts create mode 100644 src/webgl/interactions/plugins/Decelerate.ts create mode 100644 src/webgl/interactions/plugins/Drag.ts create mode 100644 src/webgl/interactions/plugins/InteractionPlugin.ts create mode 100644 src/webgl/interactions/plugins/Zoom.ts rename src/webgl/interactions/{ => v1}/decelerate.ts (98%) rename src/webgl/interactions/{ => v1}/drag.ts (98%) rename src/webgl/interactions/{ => v1}/zoom.ts (98%) diff --git a/src/types/api/options.ts b/src/types/api/options.ts index a211455b..a2f031c8 100644 --- a/src/types/api/options.ts +++ b/src/types/api/options.ts @@ -73,6 +73,11 @@ export interface Options { * @default { height: 12, width: 6 } */ arrowSize?: { height: number; width: number } + /** + * minimum hover radius for interactive strokes + * @default 2 + */ + minLineHoverRadius?: number } export interface DefaultOptions extends Concrete { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 9bb4398c..3aed2904 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -14,6 +14,53 @@ export const MIN_INTERACTION_ZOOM = 0.15 export const MIN_EDGES_ZOOM = 0.1 export const MIN_ZOOM = 3 +export const DEFAULT_ARROW = 'none' +export const DEFAULT_STROKE_WIDTH = 1 +export const DEFAULT_OPACITY = 1 +export const DEFAULT_FILL_COLOR = '#AAAAAA' + +export const DEFAULT_STROKE = { + color: DEFAULT_FILL_COLOR, + width: DEFAULT_STROKE_WIDTH +} + +export const DEFAULT_FILL_STYLE = { + color: DEFAULT_FILL_COLOR, + opacity: DEFAULT_OPACITY +} + +export const VIEWPORT_EVENT = { + POINTER: 'viewportPointer', + WHEEL: 'viewportWheel', + DRAG: 'viewportDrag', + DRAG_DECELERATE: 'viewportDragDecelerate' +} as const + +export const NODE_EVENT = { + POINTER: 'nodePointer', + DRAG: 'nodeDrag' +} as const + +export const EDGE_EVENT = { + POINTER: 'edgePointer' +} as const + +export const POINTER = { + MOVE: 'pointermove', + ENTER: 'pointerenter', + LEAVE: 'pointerleave', + UP: 'pointerup', + DOWN: 'pointerdown', + CANCEL: 'pointercancel', + OUTSIDE: 'pointerupoutside' +} as const + +export const CURSOR = { + AUTO: 'auto', + MOVE: 'move', + POINTER: 'pointer' +} as const + export const DEFAULT_OPTIONS = { maxZoom: 3, minZoom: 0.025, @@ -29,5 +76,6 @@ export const DEFAULT_OPTIONS = { maxRadius: 15, resolution: 2, arrowSize: { width: 6, height: 12 }, - defaultViewport: { x: 0, y: 0, zoom: 1 } + defaultViewport: { x: 0, y: 0, zoom: 1 }, + minLineHoverRadius: 2 } diff --git a/src/webgl/RenderObject.ts b/src/webgl/RenderObject.ts new file mode 100644 index 00000000..c8948985 --- /dev/null +++ b/src/webgl/RenderObject.ts @@ -0,0 +1,72 @@ +import { Container, DisplayObject } from 'pixi.js' +import { RenderObjectLifecycle } from '../types' + +export default abstract class RenderObject implements RenderObjectLifecycle { + mounted = false + + protected x = 0 + protected y = 0 + + protected declare abstract object: T + + constructor(protected container: Container) { + this.container = container + } + + moveTo(...args: number[]): this + moveTo(x: number, y: number): this { + if (this.x !== x) { + this.x = x + this.object.x = x + } + + if (this.y !== y) { + this.y = y + this.object.y = y + } + + return this + } + + mount(index?: number): this { + if (this.mounted) { + return this + } + + if (index !== undefined) { + this.container.addChildAt(this.object, index) + } else { + this.container.addChild(this.object) + } + + this.mounted = true + + return this + } + + unmount(): this { + if (!this.mounted) { + return this + } + + this.container.removeChild(this.object) + this.mounted = false + + return this + } + + delete(): void { + this.unmount() + this.object.destroy() + + return undefined + } + + getContainerIndex(): number { + if (this.mounted) { + return this.container.getChildIndex(this.object) + } + + return -1 + } +} diff --git a/src/webgl/RendererOptions.ts b/src/webgl/RendererOptions.ts index 142b896a..0f157927 100644 --- a/src/webgl/RendererOptions.ts +++ b/src/webgl/RendererOptions.ts @@ -19,17 +19,18 @@ export default class RendererOptions implements DefaultOptions { dragInertia: number = DEFAULT_OPTIONS.dragInertia maxRadius: number = DEFAULT_OPTIONS.maxRadius arrowSize: Dimensions = DEFAULT_OPTIONS.arrowSize + minLineHoverRadius: number = DEFAULT_OPTIONS.minLineHoverRadius constructor(defaultOptions: DefaultOptions) { this._defaultOptions = defaultOptions this.set({}) } - set({ animateViewport = true, animateNodeRadius = true, animateNodePosition = true, ...options }: Options): this { + set(options: Options): this { Object.assign(this, options, this._defaultOptions, { - animateViewport: this.coerceAnimationValue('animateViewport', animateViewport), - animateNodeRadius: this.coerceAnimationValue('animateNodeRadius', animateNodeRadius), - animateNodePosition: this.coerceAnimationValue('animateNodePosition', animateNodePosition) + animateViewport: this.coerceAnimationValue('animateViewport', options.animateViewport), + animateNodeRadius: this.coerceAnimationValue('animateNodeRadius', options.animateNodeRadius), + animateNodePosition: this.coerceAnimationValue('animateNodePosition', options.animateNodePosition) }) return this @@ -37,7 +38,7 @@ export default class RendererOptions implements DefaultOptions { private coerceAnimationValue( key: 'animateViewport' | 'animateNodePosition' | 'animateNodeRadius', - value: number | boolean + value: number | boolean | undefined = true ): number | false { return value === false || isNumber(value) ? value diff --git a/src/webgl/interactions/ApplicationEvents.ts b/src/webgl/interactions/ApplicationEvents.ts new file mode 100644 index 00000000..7c8a6291 --- /dev/null +++ b/src/webgl/interactions/ApplicationEvents.ts @@ -0,0 +1,97 @@ +import { EventHandlers } from '../../types/api' +import { EventSystem } from 'pixi.js' + +export default class ApplicationEvents { + private _eventHandlers: EventHandlers = {} + private _eventSystem: EventSystem + + constructor(eventSystem: EventSystem) { + this._eventSystem = eventSystem + } + + set(eventHandlers: EventHandlers) { + this._eventHandlers = eventHandlers + } + + get system() { + return this._eventSystem + } + get onViewportPointerEnter() { + return this._eventHandlers.onViewportPointerEnter + } + get onViewportPointerDown() { + return this._eventHandlers.onViewportPointerDown + } + get onViewportPointerMove() { + return this._eventHandlers.onViewportPointerMove + } + get onViewportDragStart() { + return this._eventHandlers.onViewportDragStart + } + get onViewportDrag() { + return this._eventHandlers.onViewportDrag + } + get onViewportDragEnd() { + return this._eventHandlers.onViewportDragEnd + } + get onViewportPointerUp() { + return this._eventHandlers.onViewportPointerUp + } + get onViewportClick() { + return this._eventHandlers.onViewportClick + } + get onViewportDoubleClick() { + return this._eventHandlers.onViewportDoubleClick + } + get onViewportPointerLeave() { + return this._eventHandlers.onViewportPointerLeave + } + get onViewportWheel() { + return this._eventHandlers.onViewportWheel + } + get onNodePointerEnter() { + return this._eventHandlers.onNodePointerEnter + } + get onNodePointerDown() { + return this._eventHandlers.onNodePointerDown + } + get onNodeDragStart() { + return this._eventHandlers.onNodeDragStart + } + get onNodeDrag() { + return this._eventHandlers.onNodeDrag + } + get onNodeDragEnd() { + return this._eventHandlers.onNodeDragEnd + } + get onNodePointerUp() { + return this._eventHandlers.onNodePointerUp + } + get onNodeClick() { + return this._eventHandlers.onNodeClick + } + get onNodeDoubleClick() { + return this._eventHandlers.onNodeDoubleClick + } + get onNodePointerLeave() { + return this._eventHandlers.onNodePointerLeave + } + get onEdgePointerEnter() { + return this._eventHandlers.onEdgePointerEnter + } + get onEdgePointerDown() { + return this._eventHandlers.onEdgePointerDown + } + get onEdgePointerUp() { + return this._eventHandlers.onEdgePointerUp + } + get onEdgePointerLeave() { + return this._eventHandlers.onEdgePointerLeave + } + get onEdgeClick() { + return this._eventHandlers.onEdgeClick + } + get onEdgeDoubleClick() { + return this._eventHandlers.onEdgeDoubleClick + } +} diff --git a/src/webgl/interactions/Interaction.ts b/src/webgl/interactions/Interaction.ts new file mode 100644 index 00000000..e3155d72 --- /dev/null +++ b/src/webgl/interactions/Interaction.ts @@ -0,0 +1,21 @@ +import { Container } from 'pixi.js' +import ApplicationEvents from './ApplicationEvents' +import RendererOptions from '../RendererOptions' + +export default abstract class Interaction { + constructor( + protected events: ApplicationEvents, + protected options: RendererOptions, + protected container: HTMLDivElement, + protected root: Container + ) { + this.events = events + this.options = options + this.container = container + this.root = root + } + + protected set cursor(cursor: string) { + this.container.style.cursor = cursor + } +} diff --git a/src/webgl/interactions/InteractionManager.ts b/src/webgl/interactions/InteractionManager.ts new file mode 100644 index 00000000..8c345a95 --- /dev/null +++ b/src/webgl/interactions/InteractionManager.ts @@ -0,0 +1,34 @@ +import { ObjectManager } from '../objects' +import { Container } from 'pixi.js' +import ApplicationEvents from './ApplicationEvents' +import RendererOptions from '../RendererOptions' +import Decelerate from './plugins/Decelerate' +import HitArea from './hitArea/HitArea' +import Drag from './plugins/Drag' +import Zoom from './plugins/Zoom' + +export default class InteractionManager { + drag: Drag + zoom: Zoom + decelerate: Decelerate + container = new Container() + manager = new ObjectManager(2000) + + constructor(events: ApplicationEvents, options: RendererOptions, container: HTMLDivElement, root: Container) { + this.drag = new Drag(events, options, container, root) + this.zoom = new Zoom(events, options, container, root) + this.decelerate = new Decelerate(events, options, container, root) + } + + get isDecelerating() { + return this.decelerate.isDecelerating + } + + get isDragging() { + return this.drag.isDragging + } + + get isZooming() { + return this.zoom.isZooming + } +} diff --git a/src/webgl/interactions/events/EventManager.ts b/src/webgl/interactions/events/EventManager.ts new file mode 100644 index 00000000..8a29aeda --- /dev/null +++ b/src/webgl/interactions/events/EventManager.ts @@ -0,0 +1,41 @@ +import { FederatedPointerEvent } from 'pixi.js' +import { Container } from 'pixi.js' +import ApplicationEvents from '../ApplicationEvents' +import RendererOptions from '../../RendererOptions' +import Interaction from '../Interaction' +import HitArea from '../hitArea/HitArea' + +export default abstract class EventsManager extends Interaction { + abstract hitArea: HitArea + protected doubleClick = false + protected doubleClickTimeout: NodeJS.Timeout | undefined + + constructor(events: ApplicationEvents, options: RendererOptions, container: HTMLDivElement, root: Container) { + super(events, options, container, root) + this.onPointerDown = this.onPointerDown.bind(this) + this.onPointerEnter = this.onPointerEnter.bind(this) + this.onPointerLeave = this.onPointerLeave.bind(this) + this.onPointerMove = this.onPointerMove.bind(this) + this.onPointerUp = this.onPointerUp.bind(this) + } + + abstract onPointerEnter(event: FederatedPointerEvent): void + abstract onPointerDown(event: FederatedPointerEvent): void + abstract onPointerUp(event: FederatedPointerEvent): void + abstract onPointerLeave(event: FederatedPointerEvent): void + abstract onPointerMove(event: FederatedPointerEvent): void + + flush() { + clearTimeout(this.doubleClickTimeout) + this.clearDoubleClick() + + return this + } + + protected clearDoubleClick() { + this.doubleClick = false + this.doubleClickTimeout = undefined + + return this + } +} diff --git a/src/webgl/interactions/hitArea/CircleHitArea.ts b/src/webgl/interactions/hitArea/CircleHitArea.ts new file mode 100644 index 00000000..67a507a5 --- /dev/null +++ b/src/webgl/interactions/hitArea/CircleHitArea.ts @@ -0,0 +1,32 @@ +import { Container, Circle as PixiCircle } from 'pixi.js' +import HitArea from './HitArea' + +export default class CircleHitArea extends HitArea { + constructor(container: Container) { + super(container, new PixiCircle()) + } + + get radius() { + return this.shape.radius + } + + set radius(radius: number) { + if (this.shape.radius !== radius) { + this.shape.radius = radius + } + } + + override moveTo(x: number, y: number) { + if (this.x !== x) { + this.x = x + this.shape.x = x + } + + if (this.y !== y) { + this.y = y + this.shape.y = y + } + + return this + } +} diff --git a/src/webgl/interactions/hitArea/HitArea.ts b/src/webgl/interactions/hitArea/HitArea.ts new file mode 100644 index 00000000..19fe46cb --- /dev/null +++ b/src/webgl/interactions/hitArea/HitArea.ts @@ -0,0 +1,36 @@ +import { AllFederatedEventMap, Container, IHitArea } from 'pixi.js' +import RenderObject from '../../RenderObject' + +export default class HitArea extends RenderObject { + shape: T + protected object: Container = new Container() + + constructor(container: Container, shape: T) { + super(container) + this.shape = shape + this.object.hitArea = shape + this.object.eventMode = 'static' + } + + listen( + type: K, + listener: (e: AllFederatedEventMap[K]) => unknown, + options?: AddEventListenerOptions | boolean + ): this { + this.object.addEventListener(type, listener, options) + return this + } + + remove( + type: K, + listener: (e: AllFederatedEventMap[K]) => any, + options?: EventListenerOptions | boolean + ): this { + this.object.removeEventListener(type, listener, options) + return this + } + + get scale() { + return this.object.scale + } +} diff --git a/src/webgl/interactions/hitArea/LineHitArea.ts b/src/webgl/interactions/hitArea/LineHitArea.ts new file mode 100644 index 00000000..e9fa1308 --- /dev/null +++ b/src/webgl/interactions/hitArea/LineHitArea.ts @@ -0,0 +1,28 @@ +import { DEFAULT_STROKE_WIDTH, movePoint } from '../../../utils' +import { Container, Polygon } from 'pixi.js' +import RendererOptions from '../../RendererOptions' +import HitArea from './HitArea' + +export default class LineHitArea extends HitArea { + private options: RendererOptions + width: number + + constructor(options: RendererOptions, container: Container, width = DEFAULT_STROKE_WIDTH) { + super(container, new Polygon()) + this.options = options + this.width = width + } + + override moveTo(x0: number, y0: number, x1: number, y1: number, angle: number) { + const radius = Math.max(this.width, this.options.minLineHoverRadius) + + this.shape.points = [ + ...movePoint(x0, y0, angle, radius), + ...movePoint(x0, y0, angle, -radius), + ...movePoint(x1, y1, angle, -radius), + ...movePoint(x1, y1, angle, radius) + ] + + return this + } +} diff --git a/src/webgl/interactions/hitArea/RectangleHitArea.ts b/src/webgl/interactions/hitArea/RectangleHitArea.ts new file mode 100644 index 00000000..2cf8d6ce --- /dev/null +++ b/src/webgl/interactions/hitArea/RectangleHitArea.ts @@ -0,0 +1,51 @@ +import { Container, Rectangle } from 'pixi.js' +import HitArea from './HitArea' + +export default class RectangleHitArea extends HitArea { + private _width: number + private _height: number + + constructor(container: Container, x = 0, y = 0, width = 200, height = 200) { + super(container, new Rectangle(x, y, width, height)) + this.x = x + this.y = y + this._width = width + this._height = height + } + + get width() { + return this._width + } + + set width(width: number) { + if (width !== this.width) { + this.width = width + this.shape.width = width + } + } + + get height() { + return this._height + } + + set height(height: number) { + if (height !== this.height) { + this.height = height + this.shape.height = height + } + } + + override moveTo(x: number, y: number) { + if (this.x !== x) { + this.x = x + this.shape.x = x + } + + if (this.y !== y) { + this.y = y + this.shape.y = y + } + + return this + } +} diff --git a/src/webgl/interactions/index.ts b/src/webgl/interactions/index.ts index 676a92e4..38097e84 100644 --- a/src/webgl/interactions/index.ts +++ b/src/webgl/interactions/index.ts @@ -1,3 +1,3 @@ -export * from './decelerate' -export * from './drag' -export * from './zoom' +export * from './v1/decelerate' +export * from './v1/drag' +export * from './v1/zoom' diff --git a/src/webgl/interactions/plugins/Decelerate.ts b/src/webgl/interactions/plugins/Decelerate.ts new file mode 100644 index 00000000..23fe8195 --- /dev/null +++ b/src/webgl/interactions/plugins/Decelerate.ts @@ -0,0 +1,98 @@ +import { VIEWPORT_EVENT } from '../../../utils' +import InteractionPlugin from './InteractionPlugin' + +export default class Decelerate extends InteractionPlugin { + private _decelerating = false + private _i = 0 + private _dx = 0 + private _dy = 0 + private _minSpeed = 0.01 + private _saved: ({ x: number; y: number; time: number } | undefined)[] = [] + + down() { + if (this.disabled) { + return + } + + this._saved = new Array(60) + this._dx = this._dy = this._i = 0 + } + + move() { + if (this.disabled) { + return + } + + this._saved[this._i++] = { + x: this.viewport.x, + y: this.viewport.y, + time: performance.now() + } + + if (this._i > 60) { + this._saved.splice(0, 30) + this._i = 30 + } + } + + up() { + if (this._i > 0) { + const now = performance.now() + for (const saved of this._saved) { + if (saved !== undefined && saved.time >= now - 100) { + const time = now - saved.time + this._dx = (this.viewport.x - saved.x) / time + this._dy = (this.viewport.y - saved.y) / time + break + } + } + } + } + + update(elapsed: number) { + if (this.disabled || this.dragInertia === 0) { + return + } + + let dx: number | undefined, dy: number | undefined + + if (this._dx) { + dx = this._dx * elapsed * 8 + this._dx *= this.dragInertia + if (Math.abs(this._dx) < this._minSpeed) { + this._dx = 0 + } + } + + if (this._dy) { + dy = this._dy * elapsed * 8 + this._dy *= this.dragInertia + if (Math.abs(this._dy) < this._minSpeed) { + this._dy = 0 + } + } + + if (dx || dy) { + this._decelerating = true + this.events.onViewportDrag?.({ + type: VIEWPORT_EVENT.DRAG_DECELERATE, + dx: dx ?? 0, + dy: dy ?? 0 + }) + } else { + this._decelerating = false + } + } + + get isDecelerating() { + return this._decelerating + } + + private get disabled() { + return this.events.onViewportDrag === undefined || this._paused + } + + private get dragInertia() { + return this.options.dragInertia + } +} diff --git a/src/webgl/interactions/plugins/Drag.ts b/src/webgl/interactions/plugins/Drag.ts new file mode 100644 index 00000000..4855ad3d --- /dev/null +++ b/src/webgl/interactions/plugins/Drag.ts @@ -0,0 +1,90 @@ +import { CURSOR, VIEWPORT_EVENT } from '../../../utils' +import { ViewportDragEvent } from '../../../types/api' +import { FederatedPointerEvent } from 'pixi.js' +import InteractionPlugin from './InteractionPlugin' + +export default class Drag extends InteractionPlugin { + private _dragging = false + private _last?: { x: number; y: number } + private _current?: number + private _dx = 0 + private _dy = 0 + + start(event: FederatedPointerEvent) { + if (this.disabled) { + return + } + + this.cursor = CURSOR.MOVE + this._last = { x: event.global.x, y: event.global.y } + this._current = event.pointerId + } + + drag(event: FederatedPointerEvent) { + if (this.disabled) { + return + } + + if (!!this._last && this._current === event.pointerId) { + const { x, y } = event.global + const dx = (this._last.x - x) / this.viewport.zoom + const dy = (this._last.y - y) / this.viewport.zoom + + if (this.isDragging || Math.abs(dx) >= 5 || Math.abs(dy) >= 5) { + this._last = { x, y } + this._dx = dx + this._dy = dy + + if (!this.isDragging) { + this.events.onViewportDragStart?.(this.getViewportDragEvent(event)) + this._dragging = true + } + + this.events.onViewportDrag?.(this.getViewportDragEvent(event)) + } + } + } + + end(event: FederatedPointerEvent) { + const isDragging = this.isDragging + + if (this.disabled) { + return + } + + this.cursor = CURSOR.AUTO + this._dragging = false + this._last = this._current = undefined + this._dx = this._dy = 0 + + if (isDragging) { + this.events.onViewportDragEnd?.(this.getViewportDragEvent(event)) + } + } + + get isDragging() { + return this._dragging + } + + private get disabled() { + return this.events.onViewportDrag === undefined || this._paused + } + + private getViewportDragEvent(event: FederatedPointerEvent): ViewportDragEvent { + const local = this.root.toLocal(event.global) + return { + type: VIEWPORT_EVENT.DRAG, + x: local.x, + y: local.y, + clientX: event.clientX, + clientY: event.clientY, + dx: this.isDragging ? this._dx : 0, + dy: this.isDragging ? this._dy : 0, + target: this.viewport, + altKey: event.altKey, + ctrlKey: event.ctrlKey, + metaKey: event.metaKey, + shiftKey: event.shiftKey + } + } +} diff --git a/src/webgl/interactions/plugins/InteractionPlugin.ts b/src/webgl/interactions/plugins/InteractionPlugin.ts new file mode 100644 index 00000000..43334591 --- /dev/null +++ b/src/webgl/interactions/plugins/InteractionPlugin.ts @@ -0,0 +1,27 @@ +import { Container } from 'pixi.js' +import ApplicationEvents from '../ApplicationEvents' +import RendererOptions from 'src/webgl/RendererOptions' +import Interaction from '../Interaction' + +export default abstract class InteractionPlugin extends Interaction { + protected _paused = false + + constructor(events: ApplicationEvents, options: RendererOptions, container: HTMLDivElement, root: Container) { + super(events, options, container, root) + } + + pause() { + this._paused = true + return this + } + + resume() { + this._paused = false + return this + } + + protected get viewport() { + // TODO -> !! + return this.options.defaultViewport + } +} diff --git a/src/webgl/interactions/plugins/Zoom.ts b/src/webgl/interactions/plugins/Zoom.ts new file mode 100644 index 00000000..ab6a8eed --- /dev/null +++ b/src/webgl/interactions/plugins/Zoom.ts @@ -0,0 +1,82 @@ +import { VIEWPORT_EVENT, clampZoom } from '../../../utils' +import { Point } from 'pixi.js' +import InteractionPlugin from './InteractionPlugin' + +/** + * zoom logic is based largely on the excellent [pixi-viewport](https://github.com/davidfig/pixi-viewport) + * specificially, the [Wheel Plugin](https://github.com/davidfig/pixi-viewport/blob/eb00aafebca6f9d9233a6b537d7d418616bb866e/src/plugins/wheel.js) + */ + +export default class Zoom extends InteractionPlugin { + private _zooming = false + + wheel(event: WheelEvent) { + if (this.events.onViewportWheel === undefined) { + return + } + + event.preventDefault() + event.stopPropagation() + + if (this._paused) { + return + } + + this._zooming = true + + const viewport = this.viewport + const step = (-event.deltaY * (event.deltaMode ? 20 : 1)) / 500 + const change = Math.pow(2, 1.1 * step) + + const zoomStart = viewport.zoom + const zoomEnd = clampZoom(this.minZoom, this.maxZoom, zoomStart * change) + + if ((step > 0 && zoomStart >= this.maxZoom) || (step < 0 && zoomStart <= this.minZoom)) { + return + } + + const globalStart = new Point() + this.events.system.mapPositionToPoint(globalStart, event.clientX, event.clientY) + globalStart.x /= 2 + globalStart.y /= 2 + + const localStart = this.root.toLocal(globalStart) + this.setZoom(zoomEnd) + + const globalEnd = this.root.toGlobal(localStart) + this.setZoom(zoomStart) + + this.events.onViewportWheel({ + type: VIEWPORT_EVENT.WHEEL, + x: localStart.x, + y: localStart.y, + clientX: event.clientX, + clientY: event.clientY, + dx: (viewport.x * zoomStart - viewport.x * zoomEnd - (globalStart.x - globalEnd.x)) / zoomEnd, + dy: (viewport.y * zoomStart - viewport.y * zoomEnd - (globalStart.y - globalEnd.y)) / zoomEnd, + dz: zoomEnd - zoomStart + }) + } + + end() { + this._zooming = false + } + + get isZooming() { + return this._zooming + } + + private get minZoom() { + return this.options.minZoom + } + + private get maxZoom() { + return this.options.maxZoom + } + + private setZoom(_zoom: number) { + // TODO: implement this + // this.renderer.viewportRenderer.setZoom(zoom) + return this + } +} diff --git a/src/webgl/interactions/decelerate.ts b/src/webgl/interactions/v1/decelerate.ts similarity index 98% rename from src/webgl/interactions/decelerate.ts rename to src/webgl/interactions/v1/decelerate.ts index cc0f8538..227b03a6 100644 --- a/src/webgl/interactions/decelerate.ts +++ b/src/webgl/interactions/v1/decelerate.ts @@ -1,4 +1,4 @@ -import { Renderer } from '..' +import { Renderer } from '../..' /** * deceleration logic is based largely on the excellent [pixi-viewport](https://github.com/davidfig/pixi-viewport) diff --git a/src/webgl/interactions/drag.ts b/src/webgl/interactions/v1/drag.ts similarity index 98% rename from src/webgl/interactions/drag.ts rename to src/webgl/interactions/v1/drag.ts index e8a5f06f..9a40ca95 100644 --- a/src/webgl/interactions/drag.ts +++ b/src/webgl/interactions/v1/drag.ts @@ -1,5 +1,5 @@ import { FederatedPointerEvent } from 'pixi.js' -import { Renderer } from '..' +import { Renderer } from '../..' /** * drag logic is based largely on the excellent [pixi-viewport](https://github.com/davidfig/pixi-viewport) diff --git a/src/webgl/interactions/zoom.ts b/src/webgl/interactions/v1/zoom.ts similarity index 98% rename from src/webgl/interactions/zoom.ts rename to src/webgl/interactions/v1/zoom.ts index 0ab50900..ad6ed96e 100644 --- a/src/webgl/interactions/zoom.ts +++ b/src/webgl/interactions/v1/zoom.ts @@ -1,5 +1,5 @@ import { Point } from 'pixi.js' -import { Renderer } from '..' +import { Renderer } from '../..' /** * zoom logic is based largely on the excellent [pixi-viewport](https://github.com/davidfig/pixi-viewport) From d66a8be4b0312e96ff2bd69d43c22dcf3bb64455 Mon Sep 17 00:00:00 2001 From: Mikey Gower Date: Fri, 2 Feb 2024 09:21:33 -0500 Subject: [PATCH 4/4] drop Concrete type --- src/types/api/options.ts | 3 +-- src/types/internal.ts | 4 ---- src/webgl/RendererOptions.ts | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/types/api/options.ts b/src/types/api/options.ts index a2f031c8..ec451bf5 100644 --- a/src/types/api/options.ts +++ b/src/types/api/options.ts @@ -1,5 +1,4 @@ import { Viewport } from '.' -import { Concrete } from '../internal' // renderer options export interface Options { @@ -80,7 +79,7 @@ export interface Options { minLineHoverRadius?: number } -export interface DefaultOptions extends Concrete { +export interface DefaultOptions extends Required { defaultViewport: Viewport animateViewport: number | false animateNodePosition: number | false diff --git a/src/types/internal.ts b/src/types/internal.ts index 7c249b08..591b1323 100644 --- a/src/types/internal.ts +++ b/src/types/internal.ts @@ -4,10 +4,6 @@ export type Extend = { [K in Exclude]: T[K] } & R -export type Concrete = { - [Property in keyof Type]-?: Type[Property] -} - export interface RenderObject { mounted: boolean // moveTo(...args: number[]): this diff --git a/src/webgl/RendererOptions.ts b/src/webgl/RendererOptions.ts index 0f157927..5620757a 100644 --- a/src/webgl/RendererOptions.ts +++ b/src/webgl/RendererOptions.ts @@ -1,5 +1,5 @@ import { Options, DefaultOptions, Viewport, Dimensions } from '../types/api' -import { DEFAULT_OPTIONS, isNumber } from 'src/utils' +import { DEFAULT_OPTIONS, isNumber } from '../utils' export default class RendererOptions implements DefaultOptions { private _defaultOptions: DefaultOptions