From 6c208b917a9f2867dec749c76d8516354b0ebdd0 Mon Sep 17 00:00:00 2001 From: Daniel Bolin Date: Wed, 30 Oct 2024 13:35:40 -0400 Subject: [PATCH 1/3] refactor(node-dist-vis): :truck: Move models into a secondary entrypoint --- libs/node-dist-vis/models/README.md | 3 +++ libs/node-dist-vis/models/ng-package.json | 5 ++++ libs/node-dist-vis/models/src/index.ts | 6 +++++ .../models => models/src/lib}/color-map.ts | 0 .../models => models/src/lib}/data-view.ts | 0 .../lib/models => models/src/lib}/edges.ts | 0 .../lib/models => models/src/lib}/filters.ts | 0 .../lib/models => models/src/lib}/nodes.ts | 0 .../lib/models => models/src/lib}/utils.ts | 0 .../models => models/src/lib}/view-mode.ts | 0 libs/node-dist-vis/src/index.ts | 2 +- .../src/lib/deckgl/controller.ts | 2 +- libs/node-dist-vis/src/lib/deckgl/edges.ts | 13 +++++++---- libs/node-dist-vis/src/lib/deckgl/nodes.ts | 6 +---- .../node-dist-vis/src/lib/deckgl/scale-bar.ts | 2 +- .../src/lib/deckgl/utils/color-coding.ts | 4 ++-- .../src/lib/deckgl/utils/filters.ts | 2 +- .../node-dist-vis/node-dist-vis.component.ts | 23 ++++++++++++++----- libs/node-dist-vis/tsconfig.lib.json | 4 ++-- tsconfig.base.json | 3 +++ 20 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 libs/node-dist-vis/models/README.md create mode 100644 libs/node-dist-vis/models/ng-package.json create mode 100644 libs/node-dist-vis/models/src/index.ts rename libs/node-dist-vis/{src/lib/models => models/src/lib}/color-map.ts (100%) rename libs/node-dist-vis/{src/lib/models => models/src/lib}/data-view.ts (100%) rename libs/node-dist-vis/{src/lib/models => models/src/lib}/edges.ts (100%) rename libs/node-dist-vis/{src/lib/models => models/src/lib}/filters.ts (100%) rename libs/node-dist-vis/{src/lib/models => models/src/lib}/nodes.ts (100%) rename libs/node-dist-vis/{src/lib/models => models/src/lib}/utils.ts (100%) rename libs/node-dist-vis/{src/lib/models => models/src/lib}/view-mode.ts (100%) diff --git a/libs/node-dist-vis/models/README.md b/libs/node-dist-vis/models/README.md new file mode 100644 index 000000000..1bee9a665 --- /dev/null +++ b/libs/node-dist-vis/models/README.md @@ -0,0 +1,3 @@ +# @hra-ui/node-dist-vis/models + +Secondary entry point of `@hra-ui/node-dist-vis`. It can be used by importing from `@hra-ui/node-dist-vis/models`. diff --git a/libs/node-dist-vis/models/ng-package.json b/libs/node-dist-vis/models/ng-package.json new file mode 100644 index 000000000..c781f0df4 --- /dev/null +++ b/libs/node-dist-vis/models/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "src/index.ts" + } +} diff --git a/libs/node-dist-vis/models/src/index.ts b/libs/node-dist-vis/models/src/index.ts new file mode 100644 index 000000000..782e9e789 --- /dev/null +++ b/libs/node-dist-vis/models/src/index.ts @@ -0,0 +1,6 @@ +export * from './lib/color-map'; +export * from './lib/data-view'; +export * from './lib/edges'; +export * from './lib/filters'; +export * from './lib/nodes'; +export * from './lib/view-mode'; diff --git a/libs/node-dist-vis/src/lib/models/color-map.ts b/libs/node-dist-vis/models/src/lib/color-map.ts similarity index 100% rename from libs/node-dist-vis/src/lib/models/color-map.ts rename to libs/node-dist-vis/models/src/lib/color-map.ts diff --git a/libs/node-dist-vis/src/lib/models/data-view.ts b/libs/node-dist-vis/models/src/lib/data-view.ts similarity index 100% rename from libs/node-dist-vis/src/lib/models/data-view.ts rename to libs/node-dist-vis/models/src/lib/data-view.ts diff --git a/libs/node-dist-vis/src/lib/models/edges.ts b/libs/node-dist-vis/models/src/lib/edges.ts similarity index 100% rename from libs/node-dist-vis/src/lib/models/edges.ts rename to libs/node-dist-vis/models/src/lib/edges.ts diff --git a/libs/node-dist-vis/src/lib/models/filters.ts b/libs/node-dist-vis/models/src/lib/filters.ts similarity index 100% rename from libs/node-dist-vis/src/lib/models/filters.ts rename to libs/node-dist-vis/models/src/lib/filters.ts diff --git a/libs/node-dist-vis/src/lib/models/nodes.ts b/libs/node-dist-vis/models/src/lib/nodes.ts similarity index 100% rename from libs/node-dist-vis/src/lib/models/nodes.ts rename to libs/node-dist-vis/models/src/lib/nodes.ts diff --git a/libs/node-dist-vis/src/lib/models/utils.ts b/libs/node-dist-vis/models/src/lib/utils.ts similarity index 100% rename from libs/node-dist-vis/src/lib/models/utils.ts rename to libs/node-dist-vis/models/src/lib/utils.ts diff --git a/libs/node-dist-vis/src/lib/models/view-mode.ts b/libs/node-dist-vis/models/src/lib/view-mode.ts similarity index 100% rename from libs/node-dist-vis/src/lib/models/view-mode.ts rename to libs/node-dist-vis/models/src/lib/view-mode.ts diff --git a/libs/node-dist-vis/src/index.ts b/libs/node-dist-vis/src/index.ts index 80d906f0e..b207e7c62 100644 --- a/libs/node-dist-vis/src/index.ts +++ b/libs/node-dist-vis/src/index.ts @@ -5,6 +5,6 @@ import { provideExperimentalZonelessChangeDetection } from '@angular/core'; export * from './lib/node-dist-vis/node-dist-vis.component'; /** Custom element definition for CdeVisualizationComponent */ -export const CdeVisualizationElement = createCustomElement('hra-node-dist-vis', NodeDistVisComponent, { +export const CdeVisualizationElement = await createCustomElement('hra-node-dist-vis', NodeDistVisComponent, { providers: [provideExperimentalZonelessChangeDetection()], }); diff --git a/libs/node-dist-vis/src/lib/deckgl/controller.ts b/libs/node-dist-vis/src/lib/deckgl/controller.ts index 5543fec62..9a51180ba 100644 --- a/libs/node-dist-vis/src/lib/deckgl/controller.ts +++ b/libs/node-dist-vis/src/lib/deckgl/controller.ts @@ -1,6 +1,6 @@ import { computed, Signal } from '@angular/core'; import { DeckProps } from '@deck.gl/core/typed'; -import { ViewMode } from '../models/view-mode'; +import { ViewMode } from '@hra-ui/node-dist-vis/models'; /** Initial/default controller options */ const DEFAULT_CONTROLLER_OPTIONS: DeckProps['controller'] = true; diff --git a/libs/node-dist-vis/src/lib/deckgl/edges.ts b/libs/node-dist-vis/src/lib/deckgl/edges.ts index 56987e4b5..a2e583149 100644 --- a/libs/node-dist-vis/src/lib/deckgl/edges.ts +++ b/libs/node-dist-vis/src/lib/deckgl/edges.ts @@ -2,11 +2,14 @@ import { computed, Signal } from '@angular/core'; import { COORDINATE_SYSTEM } from '@deck.gl/core/typed'; import { DataFilterExtension, DataFilterExtensionProps } from '@deck.gl/extensions/typed'; import { LineLayer } from '@deck.gl/layers/typed'; -import { ColorMapView } from '../models/color-map'; -import { AnyData, AnyDataEntry } from '../models/data-view'; -import { EdgesView } from '../models/edges'; -import { NodeFilterView } from '../models/filters'; -import { NodesView } from '../models/nodes'; +import { + AnyData, + AnyDataEntry, + ColorMapView, + EdgesView, + NodeFilterView, + NodesView, +} from '@hra-ui/node-dist-vis/models'; import { createColorAccessor } from './utils/color-coding'; import { createNodeFilterAccessor, FILTER_RANGE } from './utils/filters'; import { createScaledPositionAccessor } from './utils/position-scaling'; diff --git a/libs/node-dist-vis/src/lib/deckgl/nodes.ts b/libs/node-dist-vis/src/lib/deckgl/nodes.ts index c0cec4f6e..3a0f1c0a1 100644 --- a/libs/node-dist-vis/src/lib/deckgl/nodes.ts +++ b/libs/node-dist-vis/src/lib/deckgl/nodes.ts @@ -2,11 +2,7 @@ import { computed, Signal } from '@angular/core'; import { AccessorContext, COORDINATE_SYSTEM } from '@deck.gl/core/typed'; import { DataFilterExtension, DataFilterExtensionProps } from '@deck.gl/extensions/typed'; import { PointCloudLayer } from '@deck.gl/layers/typed'; -import { ColorMapView } from '../models/color-map'; -import { AnyData } from '../models/data-view'; -import { NodeFilterView } from '../models/filters'; -import { NodesView } from '../models/nodes'; -import { ViewMode } from '../models/view-mode'; +import { AnyData, ColorMapView, NodeFilterView, NodesView, ViewMode } from '@hra-ui/node-dist-vis/models'; import { createColorAccessor } from './utils/color-coding'; import { createNodeFilterAccessor, FILTER_RANGE } from './utils/filters'; import { createScaledPositionAccessor } from './utils/position-scaling'; diff --git a/libs/node-dist-vis/src/lib/deckgl/scale-bar.ts b/libs/node-dist-vis/src/lib/deckgl/scale-bar.ts index 32888a1c7..f207423b2 100644 --- a/libs/node-dist-vis/src/lib/deckgl/scale-bar.ts +++ b/libs/node-dist-vis/src/lib/deckgl/scale-bar.ts @@ -1,7 +1,7 @@ import { computed, Signal } from '@angular/core'; import { Layer } from '@deck.gl/core/typed'; +import { NodesView } from '@hra-ui/node-dist-vis/models'; import { ScaleBarLayer as ScaleBarLayerConstructor } from '@vivjs/layers'; -import { NodesView } from '../models/nodes'; /** Scale bar layer props. Not exported by `@vivjs/layers` */ type ScaleBarLayerProps = ConstructorParameters[0]; diff --git a/libs/node-dist-vis/src/lib/deckgl/utils/color-coding.ts b/libs/node-dist-vis/src/lib/deckgl/utils/color-coding.ts index 23cab150e..26e7d2efb 100644 --- a/libs/node-dist-vis/src/lib/deckgl/utils/color-coding.ts +++ b/libs/node-dist-vis/src/lib/deckgl/utils/color-coding.ts @@ -1,6 +1,6 @@ -import { AccessorContext, AccessorFunction, Color } from '@deck.gl/core/typed'; -import { ColorMap } from '../../models/color-map'; import { colorCategories } from '@deck.gl/carto/typed'; +import { AccessorContext, AccessorFunction, Color } from '@deck.gl/core/typed'; +import { ColorMap } from '@hra-ui/node-dist-vis/models'; /** Color format expected by `colorCategories` */ type Color2 = [r: number, g: number, b: number, a?: number]; diff --git a/libs/node-dist-vis/src/lib/deckgl/utils/filters.ts b/libs/node-dist-vis/src/lib/deckgl/utils/filters.ts index db088b813..7fd7cc6e6 100644 --- a/libs/node-dist-vis/src/lib/deckgl/utils/filters.ts +++ b/libs/node-dist-vis/src/lib/deckgl/utils/filters.ts @@ -1,5 +1,5 @@ import { AccessorFunction } from '@deck.gl/core/typed'; -import { NodeFilterPredFn } from '../../models/filters'; +import { NodeFilterPredFn } from '@hra-ui/node-dist-vis/models'; /** Value used to indicate that an item is in the filter */ const FILTER_INCLUDE_VALUE = 1; diff --git a/libs/node-dist-vis/src/lib/node-dist-vis/node-dist-vis.component.ts b/libs/node-dist-vis/src/lib/node-dist-vis/node-dist-vis.component.ts index e7629c60f..3344d9ef8 100644 --- a/libs/node-dist-vis/src/lib/node-dist-vis/node-dist-vis.component.ts +++ b/libs/node-dist-vis/src/lib/node-dist-vis/node-dist-vis.component.ts @@ -12,17 +12,28 @@ import { viewChild, } from '@angular/core'; import { DeckProps, OrbitView, OrbitViewState, PickingInfo, View } from '@deck.gl/core/typed'; +import { + AnyData, + AnyDataEntry, + ColorMapEntry, + ColorMapView, + EdgeKeysInput, + EdgesInput, + KeyMapping, + loadColorMap, + loadEdges, + loadNodeFilter, + loadNodes, + NodeFilterInput, + NodeKeysInput, + NodesInput, + ViewMode, +} from '@hra-ui/node-dist-vis/models'; import { createController } from '../deckgl/controller'; import { createDeck } from '../deckgl/deck'; import { createEdgesLayer } from '../deckgl/edges'; import { createNodesLayer } from '../deckgl/nodes'; import { createScaleBarLayer } from '../deckgl/scale-bar'; -import { ColorMapEntry, ColorMapView, loadColorMap } from '../models/color-map'; -import { AnyData, AnyDataEntry, KeyMapping } from '../models/data-view'; -import { EdgeKeysInput, EdgesInput, loadEdges } from '../models/edges'; -import { loadNodeFilter, NodeFilterInput } from '../models/filters'; -import { loadNodes, NodeKeysInput, NodesInput } from '../models/nodes'; -import { ViewMode } from '../models/view-mode'; /** CursorState is not exported by deckgl */ type CursorState = Parameters>[0]; diff --git a/libs/node-dist-vis/tsconfig.lib.json b/libs/node-dist-vis/tsconfig.lib.json index 4cab05d46..b55f7284a 100644 --- a/libs/node-dist-vis/tsconfig.lib.json +++ b/libs/node-dist-vis/tsconfig.lib.json @@ -7,6 +7,6 @@ "inlineSources": true, "types": [] }, - "exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"], - "include": ["src/**/*.ts"] + "exclude": ["**/*.spec.ts", "test-setup.ts", "jest.config.ts", "**/*.test.ts"], + "include": ["**/*.ts"] } diff --git a/tsconfig.base.json b/tsconfig.base.json index 005cbf3c8..bd2a051a7 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -167,6 +167,9 @@ "@hra-ui/node-dist-vis": [ "libs/node-dist-vis/src/index.ts" ], + "@hra-ui/node-dist-vis/models": [ + "libs/node-dist-vis/models/src/index.ts" + ], "@hra-ui/services": [ "libs/services/src/index.ts" ], From 8e4cc38abf8c8f46796aafdf72ba60f9192c611e Mon Sep 17 00:00:00 2001 From: Daniel Bolin Date: Thu, 31 Oct 2024 13:48:04 -0400 Subject: [PATCH 2/3] refactor(cde-visualization): Initial node-dist-vis integration (WIP) --- apps/cde-ui/project.json | 2 +- apps/cde-ui/tsconfig.json | 6 +- libs/cde-visualization/project.json | 16 +- libs/cde-visualization/src/index.ts | 5 +- .../cde-visualization.component.html | 16 +- .../cde-visualization.component.ts | 246 +++++++++--------- .../metadata/metadata.component.html | 8 +- .../components/metadata/metadata.component.ts | 2 +- .../node-dist-visualization.component.ts | 106 ++------ .../src/lib/models/metadata.ts | 39 ++- libs/cde-visualization/tsconfig.json | 1 - libs/node-dist-vis/models/src/index.ts | 1 + .../node-dist-vis/models/src/lib/color-map.ts | 27 +- libs/node-dist-vis/models/src/lib/edges.ts | 29 ++- libs/node-dist-vis/models/src/lib/nodes.ts | 17 +- libs/node-dist-vis/models/src/lib/utils.ts | 17 ++ libs/node-dist-vis/src/index.ts | 11 +- .../node-dist-vis/node-dist-vis.component.ts | 14 + 18 files changed, 286 insertions(+), 277 deletions(-) diff --git a/apps/cde-ui/project.json b/apps/cde-ui/project.json index 6bb88ed6f..b4d88e169 100644 --- a/apps/cde-ui/project.json +++ b/apps/cde-ui/project.json @@ -4,7 +4,7 @@ "projectType": "application", "prefix": "cde", "sourceRoot": "apps/cde-ui/src", - "tags": ["app", "cde"], + "tags": ["type:app", "project:cde"], "targets": { "build": { "executor": "@nx/angular:application", diff --git a/apps/cde-ui/tsconfig.json b/apps/cde-ui/tsconfig.json index 44decba5d..a28fec9ac 100644 --- a/apps/cde-ui/tsconfig.json +++ b/apps/cde-ui/tsconfig.json @@ -14,13 +14,13 @@ "include": [], "references": [ { - "path": "./tsconfig.app.json" + "path": "./tsconfig.editor.json" }, { - "path": "./tsconfig.spec.json" + "path": "./tsconfig.app.json" }, { - "path": "./tsconfig.editor.json" + "path": "./tsconfig.spec.json" } ], "extends": "../../tsconfig.base.json", diff --git a/libs/cde-visualization/project.json b/libs/cde-visualization/project.json index c3eff1b37..9e3b1f82c 100644 --- a/libs/cde-visualization/project.json +++ b/libs/cde-visualization/project.json @@ -7,20 +7,10 @@ "tags": ["type:lib", "project:cde", "webcomponent"], "targets": { "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], + "executor": "@nx/angular:package", + "outputs": ["{workspaceRoot}/dist/{projectRoot}"], "options": { - "outputPath": "dist/libs/cde-visualization", - "index": "libs/cde-visualization/src/index.html", - "main": "libs/cde-visualization/src/index.ts", - "polyfills": ["zone.js"], - "tsConfig": "libs/cde-visualization/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "styles": ["libs/cde-visualization/src/styles.scss"], - "stylePreprocessorOptions": { - "includePaths": ["node_modules/", "libs/design-system/styles"] - }, - "scripts": [] + "project": "libs/cde-visualization/ng-package.json" }, "configurations": { "production": { diff --git a/libs/cde-visualization/src/index.ts b/libs/cde-visualization/src/index.ts index 6cd49f8cc..f1fed453a 100644 --- a/libs/cde-visualization/src/index.ts +++ b/libs/cde-visualization/src/index.ts @@ -18,14 +18,11 @@ export * from './lib/shared/tooltip-position'; export * from './lib/services/data/color-map-loader.service'; /** Type for CdeVisualizationElement instance */ -export type CdeVisualizationElement = InstanceType; +export type CdeVisualizationElement = InstanceType>; /** Input properties for CdeVisualizationComponent */ export type CdeVisualizationElementProps = InputProps; -/** Constructor type for CdeVisualizationElement */ -export type CdeVisualizationElementConstructor = Awaited; - /** Custom element definition for CdeVisualizationComponent */ export const CdeVisualizationElement = createCustomElement('cde-visualization', CdeVisualizationComponent, { providers: [ diff --git a/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.html b/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.html index eb190a075..edd3eee63 100644 --- a/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.html +++ b/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.html @@ -9,11 +9,11 @@ - @if (isFieldVisible('colorMap')) { + @if (isFieldVisible('colorMapFileName')) {
Color Map - {{ metadata().colorMap | defaultTo: 'Default' }} + {{ metadata().colorMapFileName | defaultTo: 'Default' }}
} @if (isFieldVisible('organ')) { diff --git a/libs/cde-visualization/src/lib/components/metadata/metadata.component.ts b/libs/cde-visualization/src/lib/components/metadata/metadata.component.ts index 9541cbdb4..0ffbc12df 100644 --- a/libs/cde-visualization/src/lib/components/metadata/metadata.component.ts +++ b/libs/cde-visualization/src/lib/components/metadata/metadata.component.ts @@ -11,7 +11,7 @@ import { TooltipCardComponent, TooltipContent } from '@hra-ui/design-system/tool /** List of metadata fields that can be hidden */ const HIDABLE_FIELDS: (keyof Metadata)[] = [ 'title', - 'colorMap', + 'colorMapFileName', 'organ', 'technology', 'sex', diff --git a/libs/cde-visualization/src/lib/components/node-dist-visualization/node-dist-visualization.component.ts b/libs/cde-visualization/src/lib/components/node-dist-visualization/node-dist-visualization.component.ts index 711a1bb25..89121c55a 100644 --- a/libs/cde-visualization/src/lib/components/node-dist-visualization/node-dist-visualization.component.ts +++ b/libs/cde-visualization/src/lib/components/node-dist-visualization/node-dist-visualization.component.ts @@ -18,55 +18,13 @@ import { } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; -import 'hra-node-dist-vis/docs/hra-node-dist-vis.wc.js'; -import { ColorMapEntry } from '../../models/color-map'; -import { EdgeEntry } from '../../models/edge'; +import { TooltipCardComponent, TooltipContent } from '@hra-ui/design-system/tooltip-card'; +import '@hra-ui/node-dist-vis'; +import { NodeDistVisElement } from '@hra-ui/node-dist-vis'; +import { ColorMapView, EdgesView, NodesView } from '@hra-ui/node-dist-vis/models'; import { NodeEntry } from '../../models/node'; import { FileSaverService } from '../../services/file-saver/file-saver.service'; import { TOOLTIP_POSITION_RIGHT_SIDE } from '../../shared/tooltip-position'; -import { TooltipCardComponent, TooltipContent } from '@hra-ui/design-system/tooltip-card'; - -/** Utility type to convert properties of an object into an object with a value wrapper */ -type Preactify = { - [K in keyof T]: { value: T[K] }; -}; - -/** Interface for properties required by the Node Distribution Visualization element */ -interface NodeDistVisElementProps { - /** Array of Node Entries to visualize */ - nodesData: NodeEntry[]; - /** Key used to identify the target node */ - nodeTargetKey: string; - /** Value associated with the target node */ - nodeTargetValue: string; - /** URL for the edges data (if applicable) */ - edgesUrl: string; - /** Array of Edge Entries that connect the nodes */ - edgesData: EdgeEntry[]; - /** Maximum distance allowed for edges in the visualization */ - maxEdgeDistance: number; - /** Array of color map entries for visualizing nodes */ - colorMapData: ColorMapEntry[]; - /** Key in the color map used to identify colors */ - colorMapKey: string; - /** Value key in the color map used to map colors to node values */ - colorMapValue: string; - /** Array of selected cell types for filtering the visualization */ - selection: string[]; -} - -/** Extended HTMLElement for Node Distribution Visualization with specific properties and methods */ -interface NodeDistVisElement extends HTMLElement, Preactify { - /** Method to reset the visualization view */ - resetView(): void; - /** Method to get data URL of the visualization */ - toDataUrl(type?: string, quality?: unknown): string | undefined; -} - -/** Checks if an array is non-empty */ -function isNonEmptyArray(array: T[]): boolean { - return array.length > 0; -} /** * Component for Node Distribution Visualization @@ -81,29 +39,15 @@ function isNonEmptyArray(array: T[]): boolean { schemas: [CUSTOM_ELEMENTS_SCHEMA], // Allows custom elements in the template }) export class NodeDistVisualizationComponent { - /** Input for the array of Node Entries */ - readonly nodes = model.required(); - - /** Input for the key used to target nodes */ - readonly nodeTargetKey = input.required(); + readonly nodes = input.required(); - /** Input for the value used to target nodes */ - readonly nodeTargetValue = input.required(); - - /** Input for the array of Edge Entries */ - readonly edges = model.required(); + readonly edges = model.required(); /** Input for the maximum edge distance */ readonly maxEdgeDistance = input.required(); /** Input for the color map data */ - readonly colorMap = input.required(); - - /** Input for the key in the color map */ - readonly colorMapKey = input.required(); - - /** Input for the value key in the color map */ - readonly colorMapValueKey = input.required(); + readonly colorMap = input.required(); /** Input for selected cell types */ readonly cellTypesSelection = input.required(); @@ -144,49 +88,45 @@ export class NodeDistVisualizationComponent { /** Bind data and events to the visualization element */ constructor() { - this.bindData('nodesData', this.nodes, isNonEmptyArray); - this.bindData('nodeTargetKey', this.nodeTargetKey); - this.bindData('nodeTargetValue', this.nodeTargetValue); - this.bindEvent('nodes', this.nodes, isNonEmptyArray); + console.log(NodeDistVisElement); + this.bindData('nodes', this.nodes); this.bindEvent('nodeClicked', this.nodeClick); this.bindEvent('nodeHovering', this.nodeHover); - this.bindData('edgesData', this.edges, isNonEmptyArray); + this.bindData('edges', this.edges); this.bindData('maxEdgeDistance', this.maxEdgeDistance); - this.bindEvent('edges', this.edges, isNonEmptyArray); + // this.bindEvent('edges', this.edges, isNonEmptyArray); - this.bindData('colorMapData', this.colorMap, isNonEmptyArray); - this.bindData('colorMapKey', this.colorMapKey); - this.bindData('colorMapValue', this.colorMapValueKey); + this.bindData('colorMap', this.colorMap); - this.bindData('selection', this.cellTypesSelection); + // this.bindData('selection', this.cellTypesSelection); } /** Downloads the visualization as an image */ download(): void { - const el = this.vis().nativeElement; - const url = el.toDataUrl(); - if (url) { - this.fileSaver.save(url, 'cell-distance-vis.png'); - } + // const el = this.vis().nativeElement; + // const url = el.toDataUrl(); + // if (url) { + // this.fileSaver.save(url, 'cell-distance-vis.png'); + // } } /** Resets the visualization view */ resetView(): void { - this.vis().nativeElement.resetView(); + // this.vis().nativeElement.resetView(); } /** Binds a property from the visualization element to a signal */ - private bindData( + private bindData( prop: K, - value: Signal, - selector?: (value: NodeDistVisElementProps[K]) => boolean, + value: Signal, + selector?: (value: NodeDistVisElement[K]) => boolean, ): void { effect(() => { const el = this.vis().nativeElement; const data = value(); if (selector === undefined || selector(data)) { - el[prop].value = data; + el[prop] = data; } }); } diff --git a/libs/cde-visualization/src/lib/models/metadata.ts b/libs/cde-visualization/src/lib/models/metadata.ts index 84d4e74e2..83ef6ab6a 100644 --- a/libs/cde-visualization/src/lib/models/metadata.ts +++ b/libs/cde-visualization/src/lib/models/metadata.ts @@ -1,11 +1,12 @@ -/** - * Interface for Metadata - */ +import { computed, Signal } from '@angular/core'; +import { JsonFileLoaderService } from '@hra-ui/common/fs'; +import { DataInput, loadData } from '@hra-ui/node-dist-vis/models'; + +export type MetadataInput = DataInput; +export type MetadataMixins = { [P in keyof Metadata]: Signal }; + +/** Metadata*/ export interface Metadata { - /** Name of the source file */ - sourceData?: string; - /** Name of the colormap file */ - colorMap?: string; /** Title of the visualization */ title?: string; /** Name of the organ */ @@ -22,13 +23,15 @@ export interface Metadata { pixelSize?: number; /** Creation timestamp (ms since 1/1/1970 UTC) */ creationTimestamp?: number; + /** Name of the source file */ + sourceFileName?: string; + /** Name of the colormap file */ + colorMapFileName?: string; /** Extra metadata for example datasets */ sampleExtra?: SampleMetadataExtra; } -/** - * Interface for Extra Metadata fields - */ +/** Extra sample metadata (used by example datasets) */ export interface SampleMetadataExtra { /** Sample type, generally '2D' or '3D' */ type: string; @@ -39,3 +42,19 @@ export interface SampleMetadataExtra { /** Source Data Sheet url */ sourceDataUrl: string; } + +export function loadMetadata(input: Signal, mixins: MetadataMixins): Signal { + const data = loadData(input, JsonFileLoaderService, {}); + return computed(() => { + const result = data(); + const metadata = typeof result === 'object' && result !== null ? (result as Metadata) : {}; + for (const key in mixins) { + const value = mixins[key as keyof Metadata]?.(); + if (value !== undefined) { + metadata[key as keyof Metadata] = value as never; + } + } + + return metadata; + }); +} diff --git a/libs/cde-visualization/tsconfig.json b/libs/cde-visualization/tsconfig.json index 92049739f..56deb89f6 100644 --- a/libs/cde-visualization/tsconfig.json +++ b/libs/cde-visualization/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { "target": "es2022", - "useDefineForClassFields": false, "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, diff --git a/libs/node-dist-vis/models/src/index.ts b/libs/node-dist-vis/models/src/index.ts index 782e9e789..0384e1668 100644 --- a/libs/node-dist-vis/models/src/index.ts +++ b/libs/node-dist-vis/models/src/index.ts @@ -3,4 +3,5 @@ export * from './lib/data-view'; export * from './lib/edges'; export * from './lib/filters'; export * from './lib/nodes'; +export { DataInput, loadData } from './lib/utils'; export * from './lib/view-mode'; diff --git a/libs/node-dist-vis/models/src/lib/color-map.ts b/libs/node-dist-vis/models/src/lib/color-map.ts index a47270e29..050a9857c 100644 --- a/libs/node-dist-vis/models/src/lib/color-map.ts +++ b/libs/node-dist-vis/models/src/lib/color-map.ts @@ -9,6 +9,7 @@ import { loadViewData, loadViewKeyMapping, } from './data-view'; +import { cachedAccessor } from './utils'; /** Color map input */ export type ColorMapInput = DataViewInput; @@ -45,11 +46,7 @@ export class ColorMapView extends BaseColorMapView { * * @returns A `ColorMap` */ - readonly getColorMap = () => { - if (this.colorMap) { - return this.colorMap; - } - + readonly getColorMap = cachedAccessor(this, () => { const domain: string[] = []; const range: Color[] = []; for (const obj of this) { @@ -57,9 +54,8 @@ export class ColorMapView extends BaseColorMapView { range.push(this.getCellColorFor(obj)); } - this.colorMap = { domain, range }; - return this.colorMap; - }; + return { domain, range }; + }); /** * Get the domain of the color map @@ -74,8 +70,19 @@ export class ColorMapView extends BaseColorMapView { */ readonly getRange = () => this.getColorMap().range; - /** Cached color map object */ - private colorMap?: ColorMap = undefined; + /** + * Get a mapping from type to color + * + * @returns A `Map` + */ + readonly getColorLookup = cachedAccessor(this, () => { + const lookup = new Map(); + for (const obj of this) { + lookup.set(this.getCellTypeFor(obj), this.getCellColorFor(obj)); + } + + return lookup; + }); } /** diff --git a/libs/node-dist-vis/models/src/lib/edges.ts b/libs/node-dist-vis/models/src/lib/edges.ts index e3f4d34da..bbe6861d5 100644 --- a/libs/node-dist-vis/models/src/lib/edges.ts +++ b/libs/node-dist-vis/models/src/lib/edges.ts @@ -53,7 +53,7 @@ export class EdgesView extends BaseEdgesView { * @returns The source position in format [x, y, z] */ readonly getSourcePositionAt = (index: number, info?: AccessorContext) => - this.getSourcePositionFor(this.data[index], info); + this.getSourcePositionFor(this.at(index), info); /** * Get the source position of an edge. @@ -68,7 +68,7 @@ export class EdgesView extends BaseEdgesView { obj: AnyDataEntry, info?: AccessorContext, ): [number, number, number] => { - const position = (info?.target ?? new Array(3)) as [number, number, number]; + const position = (info?.target ?? []) as [number, number, number]; position[0] = this.getX1For(obj); position[1] = this.getY1For(obj); position[2] = this.getZ1For(obj); @@ -85,7 +85,7 @@ export class EdgesView extends BaseEdgesView { * @returns The target position in format [x, y, z] */ readonly getTargetPositionAt = (index: number, info?: AccessorContext) => - this.getTargetPositionFor(this.data[index], info); + this.getTargetPositionFor(this.at(index), info); /** * Get the target position of an edge. @@ -100,12 +100,33 @@ export class EdgesView extends BaseEdgesView { obj: AnyDataEntry, info?: AccessorContext, ): [number, number, number] => { - const position = (info?.target ?? new Array(3)) as [number, number, number]; + const position = (info?.target ?? []) as [number, number, number]; position[0] = this.getX2For(obj); position[1] = this.getY2For(obj); position[2] = this.getZ2For(obj); return position; }; + + /** + * Get the distance/length of an edge + * + * @param index Index of data entry + * @returns The length of the edge + */ + readonly getDistanceAt = (index: number) => this.getDistanceFor(this.at(index)); + + /** + * Get the distance/length of an edge + * + * @param obj Raw edge data entry + * @returns The length of the edge + */ + readonly getDistanceFor = (obj: AnyDataEntry) => { + const xDiff = this.getX1For(obj) - this.getX2For(obj); + const yDiff = this.getY1For(obj) - this.getY2For(obj); + const zDiff = this.getZ1For(obj) - this.getZ2For(obj); + return Math.hypot(xDiff, yDiff, zDiff); + }; } /** diff --git a/libs/node-dist-vis/models/src/lib/nodes.ts b/libs/node-dist-vis/models/src/lib/nodes.ts index 682a63a73..ac8f57caa 100644 --- a/libs/node-dist-vis/models/src/lib/nodes.ts +++ b/libs/node-dist-vis/models/src/lib/nodes.ts @@ -10,6 +10,7 @@ import { loadViewData, loadViewKeyMapping, } from './data-view'; +import { cachedAccessor } from './utils'; /** Node view input */ export type NodesInput = DataViewInput; @@ -49,7 +50,7 @@ export class NodesView extends BaseNodesView { * @returns The position in format [x, y, z] */ readonly getPositionAt = (index: number, info?: AccessorContext) => - this.getPositionFor(this.data[index], info); + this.getPositionFor(this.at(index), info); /** * Get the position of a node. @@ -74,11 +75,7 @@ export class NodesView extends BaseNodesView { * * @returns An array of [minimum, maximum] values */ - readonly getDimensions = (): [number, number] => { - if (this.dimensions) { - return this.dimensions; - } - + readonly getDimensions = cachedAccessor(this, (): [number, number] => { let min = Number.MAX_VALUE; let max = -Number.MAX_VALUE; for (const obj of this) { @@ -89,12 +86,8 @@ export class NodesView extends BaseNodesView { max = Math.max(max, x, y, z); } - this.dimensions = [min, max]; - return this.dimensions; - }; - - /** Cached dimensions */ - private dimensions?: [number, number] = undefined; + return [min, max]; + }); } /** diff --git a/libs/node-dist-vis/models/src/lib/utils.ts b/libs/node-dist-vis/models/src/lib/utils.ts index 7b36d1f54..7800fa20f 100644 --- a/libs/node-dist-vis/models/src/lib/utils.ts +++ b/libs/node-dist-vis/models/src/lib/utils.ts @@ -16,6 +16,23 @@ export function isRecordObject(obj: unknown): obj is Record(instance: object, accessor: () => Res): () => Res { + const cacheKey = Symbol(); + const obj = instance as Record; + + return () => { + obj[cacheKey] ??= accessor(); + return obj[cacheKey]; + }; +} + /** * Tries to parse a value as json * diff --git a/libs/node-dist-vis/src/index.ts b/libs/node-dist-vis/src/index.ts index b207e7c62..0f95a9946 100644 --- a/libs/node-dist-vis/src/index.ts +++ b/libs/node-dist-vis/src/index.ts @@ -1,10 +1,13 @@ -import { createCustomElement } from '@hra-ui/webcomponents'; -import { NodeDistVisComponent } from './lib/node-dist-vis/node-dist-vis.component'; import { provideExperimentalZonelessChangeDetection } from '@angular/core'; +import { createCustomElement, InputProps } from '@hra-ui/webcomponents'; +import { NodeDistVisComponent } from './lib/node-dist-vis/node-dist-vis.component'; export * from './lib/node-dist-vis/node-dist-vis.component'; -/** Custom element definition for CdeVisualizationComponent */ -export const CdeVisualizationElement = await createCustomElement('hra-node-dist-vis', NodeDistVisComponent, { +export type NodeDistVisElement = InstanceType>; +export type NodeDistVisElementProps = InputProps; + +/** Custom element definition for NodeDistVisComponent */ +export const NodeDistVisElement = createCustomElement('hra-node-dist-vis', NodeDistVisComponent, { providers: [provideExperimentalZonelessChangeDetection()], }); diff --git a/libs/node-dist-vis/src/lib/node-dist-vis/node-dist-vis.component.ts b/libs/node-dist-vis/src/lib/node-dist-vis/node-dist-vis.component.ts index 3344d9ef8..499610197 100644 --- a/libs/node-dist-vis/src/lib/node-dist-vis/node-dist-vis.component.ts +++ b/libs/node-dist-vis/src/lib/node-dist-vis/node-dist-vis.component.ts @@ -203,6 +203,20 @@ export class NodeDistVisComponent { }); } + /** + * Creates a blob representing the image in the canvas. + * + * @param type Image format (default: image/png) + * @param quality Image quality, a number between 0 and 1 + * @returns A data blob + */ + toBlob(type?: string, quality?: number): Promise { + return new Promise((resolve) => { + this.deck().redraw('toDataUrl'); + this.canvas().toBlob(resolve, type, quality); + }); + } + /** * Get the cursor to display * From 5edec582e29ca5fea2c139bed87e1c355f262ff5 Mon Sep 17 00:00:00 2001 From: Daniel Bolin Date: Thu, 31 Oct 2024 13:49:12 -0400 Subject: [PATCH 3/3] refactor(cde-visualization): Update peer deps --- libs/cde-visualization/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/cde-visualization/package.json b/libs/cde-visualization/package.json index b00bd29ac..d053b10fc 100644 --- a/libs/cde-visualization/package.json +++ b/libs/cde-visualization/package.json @@ -17,7 +17,8 @@ "@angular/platform-browser": "18.2.1", "@angular/cdk": "18.2.1", "@angular/material": "18.2.1", - "@hra-ui/common": "0.0.1" + "@hra-ui/common": "0.0.1", + "@hra-ui/node-dist-vis": "0.0.1" }, "dependencies": {}, "sideEffects": [