UNPKG

@bimdata/2d-engine

Version:

An object oriented 2D viewer to load thousands of objects that you can select, highlight, hide, move with high performances

846 lines (755 loc) 21.5 kB
// TODO these imports will not work if @pixi is not a dependency type PIXIContainer = import("../node_modules/@pixi/display/index").Container; type PIXIMatrix = import("../node_modules/@pixi/math/index").Matrix; type PIXITransform = import("../node_modules/@pixi/math/index").Matrix; type PIXIRenderer = import("../node_modules/@pixi/core/index").Renderer; type PIXIRenderTexture = import("../node_modules/@pixi/core/index").RenderTexture; type PIXITexture = import("../node_modules/@pixi/core/index").Texture; type PIXIGeometry = import("../node_modules/@pixi/core/index").Geometry; type PIXIGraphics = import("../node_modules/@pixi/graphics/index").Graphics; /** * A factory to create a viewer */ declare module "@bimdata/2d-engine" { export default E2D.makeViewer; } interface DOMMouseEvent extends MouseEvent {} declare namespace E2D { function makeViewer(cfg: ViewerCfg): Viewer; interface ViewerCfg extends LayerOptions { canvas: HTMLCanvasElement; autoStart?: boolean; } interface Destroyable { destroy(): void; } interface Viewer extends Destroyable { readonly canvas: HTMLCanvasElement; readonly scene: Scene; readonly camera: Camera; readonly renderer: Renderer; readonly textureManager: TextureManager; readonly styles: { default: Style; selected: Style; highlight: Style }; readonly settings: Settings; readonly constants: Constants; readonly picker: Picker; readonly snapper: Snapper; readonly ticker: Ticker; readonly ui: UI; readonly utils: { readonly vector2D: Vector2DUtils; makeTimedListProcessor( callback: Function, maxTimeMs?: number ): TimedListProcessor; makeScheduler(): Scheduler; geometry: GeometryUtils; svg: SvgUtils; }; } interface SvgUtils { parsePath(d: string): E2D.PathData[]; makeModelDataFromSVG(svg: SVGElement): E2D.ModelData; } interface GeometryUtils { geometrize(viewer: Viewer, model: Model): GeometrizedModel; binarize(geometrizedModel: E2D.GeometrizedModel): ArrayBuffer; parse(buffer: ArrayBuffer): GeometrizedModel; versions: Array<Object>; getBinary(modelData: ModelData): Promise<ArrayBuffer>; } interface UI extends EventHandler<UIHandlerEvents<UIMouseEvent>>, Destroyable { pick: boolean; translatable: boolean; rotatable: boolean; scallable: boolean; } interface HTMLElementMouseEvent extends DOMMouseEvent { readonly canvasPosition: Point; readonly position: Point; readonly righClick: boolean; } interface UIMouseEvent extends HTMLElementMouseEvent { readonly object?: SceneObject; readonly previousObject?: SceneObject; readonly mouseDownCanvasPosition?: Point; readonly mouseDownPosition?: Point; } interface HTMLElementUIHandler extends EventHandler<HTMLElementUIHandlerEvents>, Destroyable { rightClick: boolean; mouseDownCanvasPosition?: Point; } type HTMLElementUIHandlerEvents = UIHandlerEvents<HTMLElementMouseEvent>; type UIHandlerEvents<T> = { click: T; mousemove: T; mousedown: T; mouseup: T; wheel: T; contextmenu: T; mouseleave: T; }; enum LINE_CAP { BUTT, ROUND, SQUARE, } enum LINE_JOIN { BEVEL, MITER, ROUND, } type Constants = { LINE_CAP: { BUTT: LINE_CAP; ROUND: LINE_CAP; SQUARE: LINE_CAP; }; LINE_JOIN: { BEVEL: LINE_JOIN; MITER: LINE_JOIN; ROUND: LINE_JOIN; }; DEFAULT_MODEL_ID: string; }; interface Vector2DUtils { distance(v1: Vector2D, v2: Vector2D): number; sub(v1: Vector2D, v2: Vector2D): Vector2D; add(v1: Vector2D, v2: Vector2D): Vector2D; normalize(v: Vector2D): Vector2D; length(v: Vector2D): number; dot(v1: Vector2D, v2: Vector2D): number; cross(v1: Vector2D, v2: Vector2D): number; /** * @param angle in radians anticlockwise. */ rotateAround(v: Vector2D, center: Vector2D, angle: number): Vector2D; /** * @returns { number } angle in radians anticlockwise. */ angle(v1: Vector2D, v2: Vector2D): number; } interface Settings { dragKey: string; rotateKey: string; clickMaxOffset: number; scaleSpeed: number; scaleDeltaMax: number; rotateSpeed: number; maxScale: number; minScale: number; fitViewRatio: number; snapMargin: number; curves: { adaptive: boolean; maxLength: number; maxSegments: number; minSegments: number; }; } interface Ticker extends EventHandler<{ tick: number }>, Scheduler { start(): void; stop(): void; running: boolean; } interface Scheduler { tasks: Map<string, Task>; run(...args: any[]): void; add(name: string, fn: Function, priority?: number, once?: boolean): void; addOnce(name: string, fn: Function, priority?: number): void; remove(name: string): boolean; clear(): void; } interface Task { name: string; fn: Function; priority: number; once: boolean; } interface Picker { pick(x: number, y: number): SceneObject | undefined; oncePickingReady(name: string, callback: Function): Promise<void>; } interface Snapper { getSnap(canvasPosition: Point, options?: SnapOptions): Point; } interface SnapOptions { canvasDistance?: number; snapVertices?: boolean; snapLines?: boolean; } /** * A camera allows to see the world. Two coordinate systems can be manipulated using it: * - the world coordinates, mentionned as `position`. * - the canvas coordinates, mentionned as `canvasPosition`. * * A camera emits events: * - "update", payload { MatrixExtended } : the camera transform. Emited when the camera is updated. * * To express the zoom level, the camera has two units: * - zoom, expressed in percentages, as 100 times the scale. * - scale ]0,+Infitiy[. * */ interface Camera extends Transformable, Positionable, Readonly<EventHandler<{ update: PIXITransform }>>, Destroyable { fitView( target: Scene | Scene[] | Model | Model[] | SceneObject | SceneObject[] ): void; /** * @param factor ]-Infinity, +Infinity[ * @param origin the zoom transform center. */ zoomIn(factor?: number, origin?: Point): void; /** * @param factor ]-Infinity, +Infinity[ * @param origin the zoom transform center. */ zoomOut(factor?: number, origin?: Point): void; /** * @param angle in degree clockwise. * @param origin the rotation transform center. */ transform: PIXIMatrix; position: Point; /** * @returns { number } the camera rotation angle in degree clockwise. */ rotation: number; /** * @returns { number } ]0, +Infinity[ */ getScale(): number; /** * @returns { number } ]0, +Infinity[ */ zoom: number; /** * Returns the position of the given canvas position. */ getPosition(canvasPosition: Point): Point; /** * Returns the canvas position of the given position. */ getCanvasPosition(position: Point): Point; getViewpoint(): Viewpoint; setViewpoint(viewpoint: Viewpoint): Viewpoint; } interface Viewpoint { x: number; y: number; /** * degree clockwise */ rotation: number; /** * ]-Infinity, +Infinity[ */ zoom: number; } interface Point { x: number; y: number; } interface ReactivePoint extends Point, ReactiveEntity { index: number; } interface Vector2D extends Point {} type Events = { [eventName: string]: Object; }; interface EventHandler<T extends Events> { on<N extends keyof T>(eventName: N, callback: (arg: T[N]) => void): number; once<N extends keyof T>( eventName: N, callback: (arg: T[N]) => void ): number; off(subscriptionId: number): void; emit<N extends keyof T>(eventName: N, payload: T[N]): void; clear(): void; } interface Typed<T> { type: T; } enum EntityType { scene = "scene", model = "model", object = "object", shape = "shape", geometry = "geometry", } interface Scene extends Readonly<EventHandler<SceneEvents>>, Typed<EntityType.scene>, Positionable { dirty: boolean; cache: Object; readonly viewer: Viewer; generateReport(): SceneReport; // Models readonly orphanObjectsModel: Model; readonly models: Model[]; readonly modelsMap: Map<string, Model>; addModel(modelData: ModelData): Promise<Model>; removeModel(id: string): boolean; _removedModels: Model[]; // Objects readonly objects: SceneObject[]; readonly objectsMap: Map<string, SceneObject>; readonly pickingIds: Map<number, SceneObject>; addObject(objectData: SceneObjectData, model?: Model): SceneObject; removeObject(objectId: string): boolean; _shapeId: number; _geometryId: number; // Objects setters showObjects(ids: string[]): void; hideObjects(ids: string[]): void; selectObjects(ids: string[]): void; deselectObjects(ids: string[]): void; highlightObjects(ids: string[]): void; unhighlightObjects(ids: string[]): void; setObjectsPickable(ids: string[]): void; setObjectsUnpickable(ids: string[]): void; // Object getters readonly shownObjects: SceneObject[]; readonly hiddenObjects: SceneObject[]; readonly selectedObjects: SceneObject[]; readonly unselectedObjects: SceneObject[]; readonly highlightedObjects: SceneObject[]; readonly unhighlightedObjects: SceneObject[]; readonly pickableObjects: SceneObject[]; readonly unpickableObjects: SceneObject[]; } type SceneEvents = { "model-added": Model; "model-removed": Model; "object-added": SceneObject; "object-removed": SceneObject; "object-update": { object: SceneObject; property: keyof SceneStateObjectData | keyof Style | "shapes"; value?: number | Array<number> | string; oldValue?: number | Array<number> | string; }; }; interface ModelData { readonly id?: string; readonly objects?: SceneObjectData[]; readonly textures?: TextureData[]; } interface TextureData { name: string; uri: string; width?: number; height?: number; resolution?: number; } /** * A model is where objects live. */ interface Model extends Transformable, Positionable, Typed<EntityType.model> { readonly id: string; dirty: boolean; readonly scene: Scene; addObject(objectData: SceneObjectData): SceneObject; removeObject(objectId: string): boolean; readonly objects: SceneObject[]; } interface GeometrizedModel { readonly id: string; objects: GeometrizedObject[]; textures: TextureData[]; } interface Translate { dx: number; dy: number; } interface SceneStateObjectData { visible: boolean; selected: boolean; highlighted: boolean; pickable: boolean; } interface SceneObjectData extends SceneStateObjectData { readonly id?: string; readonly zIndex?: number; readonly pickingZIndex?: number; readonly shapes?: ShapeData[]; readonly texts?: Text[]; readonly geometries?: Geometry[]; } interface SceneObject extends Transformable, Positionable, Typed<EntityType.object> { readonly scene: Scene; readonly model: Model; readonly id: string; readonly readonly?: boolean; readonly zIndex: number; readonly pickingId: number; readonly pickingZIndex: number; readonly pickingMargin: number; readonly shapes: Shape[]; getShape(index?: number): Shape; addShape(shapeData: ShapeData, index?: number): Shape; removeShape(index?: number): Shape; readonly texts?: Text[]; getText(index?: number): Text; addText(text: Text, index?: number): Text; removeText(index?: number): Text; readonly geometries?: Geometry[]; flags: number; } type SceneObjectFactory = ( objectData: SceneObjectData, model: Model ) => SceneObject; interface GeometrizedObject { id: string; zIndex: number; pickingZIndex: number; pickable: boolean; texts: Text; readonly: boolean; aabb: AABB; geometries: GeometryData[]; } interface GeometryData { type: "line" | "fill"; points: number[]; indices: number[]; color: number | string; alpha: number; native?: boolean; readonly aabb: AABB; } interface Geometry extends GeometryData, Positionable { readonly id: number; readonly object: SceneObject; readonly style: Style; // simplified style with only `lineColor`, `lineOpacity`, `fill` & `fillOpacity` } interface Drawable { draw( graphics: PIXIGraphics, translate: E2D.Translate, lineSegments?: number[], startPath?: boolean ): void; } interface Shape extends ReactiveEntity, Positionable { readonly id: number; readonly type: EntityType.shape; readonly object: SceneObject; readonly binary?: DataView; readonly paths?: Path[]; readonly style: Style; getPath(index?: number): Path | undefined; addPath(path: PathData, index?: number): Path; removePath(index?: number): Path | undefined; } interface ShapeData { paths: Array<PathData>; style: Style; } interface PathDataAbstract { type?: PathType; close?: boolean; start?: boolean; } type PathData = LineData | ArcData | CurveData; interface LineData extends PathDataAbstract { type: PathType.line; points: number[]; } interface Line extends Path { readonly type: PathType.line; cache: LineCache; readonly points: Point[]; getPoint(index?: number): Point | undefined; addPoint(point: Point, index?: number): Point; removePoint(index?: number): Point | undefined; } interface LineCache { aabb?: AABB; center?: Point; } interface ArcData extends PathDataAbstract { type: PathType.arc; x: number; y: number; radius: number; startAngle?: number; // (0 is at the 3 o'clock position of the arc's circle) endAngle?: number; anticlockwise?: boolean; } interface Arc extends Path { readonly type: PathType.arc; cache: ArcCache; x: number; y: number; radius: number; startAngle: number; endAngle: number; anticlockwise?: boolean; } interface ArcCache { aabb?: AABB; } interface CurveData extends PathDataAbstract { type: PathType.curve; x: number; y: number; toX: number; toY: number; cpX1: number; cpY1: number; cpX2: number; cpY2: number; } interface Curve extends Path { readonly type: PathType.curve; cache: CurveCache; x: number; y: number; /** * The x coordinate of the first control point. */ cpX1: number; /** * The y coordinate of the first control point. */ cpY1: number; /** * The x coordinate of the second control point. */ cpX2: number; /** * The y coordinate of the second control point. */ cpY2: number; toX: number; toY: number; } interface CurveCache { aabb?: AABB; center?: Point; } enum PathType { line = "line", arc = "arc", curve = "curve", } interface Path extends ReactiveEntity, Drawable, Positionable, Transformable { readonly type: PathType; getAABB(): AABB; getCenter(): Point; cache: any; readonly close: boolean; readonly start: boolean; } type AABB = [minX: number, minY: number, maxX: number, maxY: number]; interface Positionable { readonly aabb: AABB; readonly center: Point; } interface Transformable { move(position: Point): void; translate(dx: number, dy: number): void; /** * @param angle in degree, clockwise * @param origin */ rotate(angle: number, origin?: Point): void; scale(ds: number, origin?: Point): void; } interface ReactiveEntity { onUpdate?(cfg: any): void; } interface Style { // FILL fill?: number | string; // number for hex color, string for a registered texture name fillOpacity?: number; // LINE lineWidth?: number; lineColor?: number; lineOpacity?: number; lineDash?: number[]; lineCap?: LINE_CAP; lineJoin?: LINE_JOIN; nonScalingLineWidth?: boolean; performanceLine?: boolean; } interface TextureManager extends Destroyable { modelTextures: Map<string, Map<string, NamedPIXITexture>>; textureMatrix: TextureMatrix; load(modelId: string, texturesData: TextureData[]): Promise<void>; unload(modelId: string): boolean; } interface NamedPIXITexture extends PIXITexture { name: string; } interface TextureMatrix extends Readonly< EventHandler<{ update: { rotate?: number; scale?: Point; translate?: Point }; }> > { readonly matrix: PIXIMatrix; rotate(angle: number): void; scale(x: number, y?: number): void; translate(x: number, y: number): void; } interface TextStyle { fill: string; fontFamily: string | string[]; fontSize: number | string; fontStyle: string; fontWeight: string; lineHeight: number; } // other properties availables https://pixijs.download/v6.0.3/docs/PIXI.TextStyle.html interface Text extends TextStyle { content?: string; x?: number; y?: number; anchor?: [number, number]; rotation: number; scale: number; } interface SceneReport { addedObjects?: SceneObject[]; removedObjects?: SceneObject[]; visibleObjects?: SceneObject[]; selectedObjects?: SceneObject[]; highlightedObjects?: SceneObject[]; pickableObjects?: SceneObject[]; stylizedObjects?: SceneObject[]; shapedObjects?: SceneObject[]; textObjects?: SceneObject[]; } interface Renderer extends Readonly<EventHandler<RendererEvents>> { readonly viewer: Viewer; readonly renderer: PIXIRenderer; transform: PIXIMatrix; /** * @param { boolean } [force=false] The Renderer does not render each frame if nothing change. If `true`, it redraws the in memory shapes on the canvas. * @param { boolean } [clear=false] Clear the in memory shapes to redraw from scratch. */ render(force?: boolean, clear?: boolean): void; transformUpdated: boolean; offset: Translate; readonly layers: Map<string, Layer>; resize(): void; makeShapeDrawer( style: Style, textures?: Map<string, NamedPIXITexture>, textureMatrix?: PIXIMatrix ): Drawer; makeShapePickingDrawer( snap?: boolean, scaleDependant?: boolean ): ShapePickingDrawer; makeGeometryDrawer( style: Style, textures?: Map<string, NamedPIXITexture>, textureMatrix?: PIXIMatrix ): Drawer; geometrize(model: Model): GeometrizedModel; } type RendererEvents = { resize: { dx: number; dy: number }; "pre-draw": undefined; "post-draw": undefined; }; interface Drawer extends Destroyable { displayObject: PIXIContainer; clear(): void; draw(elements: Shape[] | Geometry[]): void; readonly scaleDependant: boolean; readonly initialLineWidth?: number; lineWidth?: number; } interface PickingDrawer extends Drawer { registerOnceDrawn(name: string, callback: Function): void; } interface ShapePickingDrawer extends PickingDrawer {} interface Layer extends Destroyable { name: string; visible: boolean; renderer: Renderer; renderTexture: PIXIRenderTexture; stage: PIXIContainer; resize(width: number, height: number): void; addObjects(objects: SceneObject[]): void; updateObjects(objects: SceneObject[]): void; removeObjects(objects: SceneObject): void; render(forceDirty?: boolean): void; dirty: boolean; clear(): void; } interface PickingLayer extends Layer { pickColorAtCanvasPosition( position: Point, snap?: boolean ): number | undefined; registerOnceDrawn(name: string, callback: Function): void; } interface BatchLayer extends Layer { objects: SceneObject[]; stage: DirtyableContainer; batches: Batch[]; render(forceDirty?: boolean, matrix?: PIXIMatrix): void; } interface LayerOptions { select?: boolean; highlight?: boolean; picking?: boolean; showPicking?: boolean; snap?: boolean; showSnap?: boolean; text?: boolean; } interface LayerHandlers { handleAddedObjects(): void; handleRemovedObjects(): void; handleUpdatedObjects(): void; } interface DirtyableContainer extends PIXIContainer { dirty: boolean; } interface Batch extends Destroyable { readonly drawer: Drawer; dirty: boolean; addEntities(entities: any[]): void; removeEntities(ids: number[]): void; entities: Map<number, any>; } interface EntityBatch<T> extends Batch { type: EntityType; modelId: string; key: string; zIndex: number; entities: Map<number, T>; addEntity(entities: T[]): void; } interface GeometryBatch extends EntityBatch<Geometry> { type: EntityType.geometry; } interface ShapeBatch extends EntityBatch<Shape> { type: EntityType.shape; } interface TimedListProcessor { readonly done: boolean; list: any[]; start(list: any[], onceDone?: Function): void; stop(): void; } }