From e06b90c71937d11d3bc5c762b46aea4433a1833c Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 26 Feb 2021 12:58:25 -0600 Subject: [PATCH 01/16] Starting to refactor --- src/ColormapContainerModel.ts | 64 +++++ src/FRBModel.ts | 95 +++++++ src/VariableMeshModel.ts | 67 +++++ src/WidgytsCanvasModel.ts | 62 +++++ src/WidgytsCanvasView.ts | 207 ++++++++++++++ src/widgyts.ts | 507 +++------------------------------- 6 files changed, 526 insertions(+), 476 deletions(-) create mode 100644 src/ColormapContainerModel.ts create mode 100644 src/FRBModel.ts create mode 100644 src/VariableMeshModel.ts create mode 100644 src/WidgytsCanvasModel.ts create mode 100644 src/WidgytsCanvasView.ts diff --git a/src/ColormapContainerModel.ts b/src/ColormapContainerModel.ts new file mode 100644 index 0000000..d1f6374 --- /dev/null +++ b/src/ColormapContainerModel.ts @@ -0,0 +1,64 @@ +import { WidgetModel } from '@jupyter-widgets/base'; +import { ColormapCollection } from '@data-exp-lab/yt-tools'; +import { MODULE_NAME, MODULE_VERSION } from './version'; +import { _yt_tools } from './widgyts'; + +export class ColormapContainerModel extends WidgetModel { + defaults(): any { + return { + ...super.defaults(), + colormap_values: {}, + _initialized: false, + _model_name: ColormapContainerModel.model_name, + _model_module: ColormapContainerModel.model_module, + _model_module_version: ColormapContainerModel.model_module_version + }; + } + + initialize(attributes: any, options: any): void { + super.initialize(attributes, options); + this.colormap_values = this.get('colormap_values'); + } + + async normalize( + colormap_name: string, + data_array: Float64Array, + output_array: Uint8ClampedArray, + min_val: number, + max_val: number, + take_log: boolean + ): Promise { + if (!this._initialized) { + await this.setupColormaps(); + } + const unclamped: Uint8Array = new Uint8Array(output_array.buffer); + this.colormaps.normalize( + colormap_name, + data_array, + unclamped, + min_val, + max_val, + take_log + ); + } + + private async setupColormaps(): Promise { + if (this._initialized) { + return; + } + const yt_tools = await _yt_tools; + this.colormaps = new yt_tools.ColormapCollection(); + for (const [name, values] of Object.entries(this.colormap_values)) { + const arr_values: Uint8Array = Uint8Array.from(values); + this.colormaps.add_colormap(name, arr_values); + } + this._initialized = true; + } + + colormap_values: unknown; + colormaps: ColormapCollection; + _initialized: boolean; + static model_name = 'ColormapContainerModel'; + static model_module = MODULE_NAME; + static model_module_version = MODULE_VERSION; +} diff --git a/src/FRBModel.ts b/src/FRBModel.ts new file mode 100644 index 0000000..db20a6a --- /dev/null +++ b/src/FRBModel.ts @@ -0,0 +1,95 @@ +import { + DOMWidgetModel, + ISerializers, + unpack_models +} from '@jupyter-widgets/base'; +import { FixedResolutionBuffer } from '@data-exp-lab/yt-tools'; +import { MODULE_NAME, MODULE_VERSION } from './version'; +import { VariableMeshModel } from './VariableMeshModel'; +import { _yt_tools } from './widgyts'; + +export interface IFRBViewBounds { + x_low: number; + x_high: number; + y_low: number; + y_high: number; +} + +export class FRBModel extends DOMWidgetModel { + defaults(): any { + return { + ...super.defaults(), + _model_name: FRBModel.model_name, + _model_module: FRBModel.model_module, + _model_module_version: FRBModel.model_module_version, + image_data: null, + width: 512, + height: 512, + view_center: [0.5, 0.5], + view_width: [1.0, 1.0], + frb: null, + variable_mesh_model: null + }; + } + + initialize(attributes: any, options: any): void { + super.initialize(attributes, options); + this.on_some_change(['width', 'height'], this.sizeChanged, this); + this.sizeChanged(); + } + static serializers: ISerializers = { + ...DOMWidgetModel.serializers, + variable_mesh_model: { deserialize: unpack_models } + }; + + sizeChanged(): void { + this.width = this.get('width'); + this.height = this.get('height'); + this.data_buffer = new Float64Array(this.width * this.height); + } + + calculateViewBounds(): IFRBViewBounds { + this.view_width = this.get('view_width'); + this.view_center = this.get('view_center'); + const hwidths: [number, number] = [ + this.view_width[0] / 2, + this.view_width[1] / 2 + ]; + const bounds = { + x_low: this.view_center[0] - hwidths[0], + x_high: this.view_center[0] + hwidths[0], + y_low: this.view_center[1] - hwidths[1], + y_high: this.view_center[1] + hwidths[1] + }; + return bounds; + } + + async depositDataBuffer( + variable_mesh_model: VariableMeshModel + ): Promise { + const bounds: IFRBViewBounds = this.calculateViewBounds(); + const yt_tools = await _yt_tools; + this.frb = new yt_tools.FixedResolutionBuffer( + this.width, + this.height, + bounds.x_low, + bounds.x_high, + bounds.y_low, + bounds.y_high + ); + this.frb.deposit(variable_mesh_model.variable_mesh, this.data_buffer); + return this.data_buffer; + } + + frb: FixedResolutionBuffer; + variable_mesh_model: VariableMeshModel; + data_buffer: Float64Array; + width: number; + height: number; + view_center: [number, number]; + view_width: [number, number]; + + static model_name = 'FRBModel'; + static model_module = MODULE_NAME; + static model_module_version = MODULE_VERSION; +} diff --git a/src/VariableMeshModel.ts b/src/VariableMeshModel.ts new file mode 100644 index 0000000..8b6350f --- /dev/null +++ b/src/VariableMeshModel.ts @@ -0,0 +1,67 @@ +import { DOMWidgetModel, ISerializers } from '@jupyter-widgets/base'; +import { VariableMesh } from '@data-exp-lab/yt-tools'; +import { MODULE_NAME, MODULE_VERSION } from './version'; +import { + _yt_tools, + serializeArray, + arrayDeserializerFactory, + ArraySerializers +} from './widgyts'; + +/* + * We have this as we can potentially have more than one FRB for a variable mesh + * + */ + +export class VariableMeshModel extends DOMWidgetModel { + defaults(): any { + return { + ...super.defaults(), + _model_name: VariableMeshModel.model_name, + _model_module: VariableMeshModel.model_module, + _model_module_version: VariableMeshModel.model_module_version, + px: null, + pdx: null, + py: null, + pdy: null, + val: null, + variable_mesh: null + }; + } + + initialize(attributes: any, options: any): void { + super.initialize(attributes, options); + _yt_tools.then(yt_tools => { + this.variable_mesh = new yt_tools.VariableMesh( + this.get('px'), + this.get('py'), + this.get('pdx'), + this.get('pdy'), + this.get('val') + ); + }); + } + + static serializers: ISerializers = { + ...DOMWidgetModel.serializers, + px: { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Float64Array) + }, + pdx: ArraySerializers.float64, + py: ArraySerializers.float64, + pdy: ArraySerializers.float64, + val: ArraySerializers.float64 + }; + + px: Float64Array; + pdx: Float64Array; + py: Float64Array; + pdy: Float64Array; + val: Float64Array; + variable_mesh: VariableMesh; + + static model_name = 'VariableMeshModel'; + static model_module = MODULE_NAME; + static model_module_version = MODULE_VERSION; +} diff --git a/src/WidgytsCanvasModel.ts b/src/WidgytsCanvasModel.ts new file mode 100644 index 0000000..71671d7 --- /dev/null +++ b/src/WidgytsCanvasModel.ts @@ -0,0 +1,62 @@ +import { ISerializers, unpack_models } from '@jupyter-widgets/base'; +import { CanvasModel } from 'ipycanvas'; +import { MODULE_NAME, MODULE_VERSION } from './version'; +import { VariableMeshModel } from './VariableMeshModel'; +import { FRBModel } from './FRBModel'; +import { ColormapContainerModel } from './ColormapContainerModel'; + +export class WidgytsCanvasModel extends CanvasModel { + defaults(): any { + return { + ...super.defaults(), + _model_name: WidgytsCanvasModel.model_name, + _model_module: WidgytsCanvasModel.model_module, + _model_module_version: WidgytsCanvasModel.model_module_version, + _view_name: WidgytsCanvasModel.view_name, + _view_module: WidgytsCanvasModel.view_module, + _view_module_version: WidgytsCanvasModel.view_module_version, + min_val: undefined, + max_val: undefined, + is_log: true, + colormap_name: 'viridis', + colormaps: null, + frb_model: null, + variable_mesh_model: null, + image_bitmap: undefined, + image_data: undefined, + _dirty_frb: false, + _dirty_bitmap: false + }; + } + + initialize(attributes: any, options: any): void { + super.initialize(attributes, options); + this.frb_model = this.get('frb_model'); + this.variable_mesh_model = this.get('variable_mesh_model'); + this.colormaps = this.get('colormaps'); + } + + static serializers: ISerializers = { + ...CanvasModel.serializers, + frb_model: { deserialize: unpack_models }, + variable_mesh_model: { deserialize: unpack_models }, + colormaps: { deserialize: unpack_models } + }; + + min_val: number; + max_val: number; + is_log: boolean; + colormap_name: string; + frb_model: FRBModel; + variable_mesh_model: VariableMeshModel; + colormaps: ColormapContainerModel; + _dirty_frb: boolean; + _dirty_bitmap: boolean; + + static view_name = 'WidgytsCanvasView'; + static view_module = MODULE_NAME; + static view_module_version = MODULE_VERSION; + static model_name = 'WidgytsCanvasModel'; + static model_module = MODULE_NAME; + static model_module_version = MODULE_VERSION; +} diff --git a/src/WidgytsCanvasView.ts b/src/WidgytsCanvasView.ts new file mode 100644 index 0000000..640cbb7 --- /dev/null +++ b/src/WidgytsCanvasView.ts @@ -0,0 +1,207 @@ +import { CanvasView } from 'ipycanvas'; +import { WidgytsCanvasModel } from './WidgytsCanvasModel'; + +export class WidgytsCanvasView extends CanvasView { + render(): void { + /* This is where we update stuff! + * Render in the base class will set up the ctx, but also calls + * updateCanvas, so we need to check before calling anything in there. + */ + this.drag = false; + this.locked = true; + super.render(); + this.initializeArrays().then(() => { + this.setupEventListeners(); + this.locked = false; + this.updateCanvas(); + }); + } + image_buffer: Uint8ClampedArray; + image_data: ImageData; + image_bitmap: ImageBitmap; + model: WidgytsCanvasModel; + locked: boolean; + drag: boolean; + dragStart: [number, number]; + dragStartCenter: [number, number]; + frbWidth: [number, number]; + + setupEventListeners(): void { + this.model.frb_model.on_some_change( + ['width', 'height'], + this.resizeFromFRB, + this + ); + this.model.frb_model.on_some_change( + ['view_center', 'view_width'], + this.dirtyFRB, + this + ); + this.model.on_some_change( + ['_dirty_frb', '_dirty_bitmap'], + this.updateBitmap, + this + ); + this.model.on_some_change( + ['min_val', 'max_val', 'colormap_name', 'is_log'], + this.dirtyBitmap, + this + ); + this.el.addEventListener('wheel', this.conductZoom.bind(this)); + this.el.addEventListener('mousedown', this.startDrag.bind(this)); + this.el.addEventListener('mousemove', this.conductDrag.bind(this)); + window.addEventListener('mouseup', this.endDrag.bind(this)); + } + + conductZoom(event: WheelEvent): void { + event.preventDefault(); + const view_width: [number, number] = this.model.frb_model.get('view_width'); + let n_units = 0; + if (event.deltaMode === event.DOM_DELTA_PIXEL) { + // let's say we have 10 units per image + n_units = event.deltaY / (this.frbWidth[1] / 10); + } else if (event.deltaMode === event.DOM_DELTA_LINE) { + // two lines per unit let's say + n_units = event.deltaY / 2; + } else if (event.deltaMode === event.DOM_DELTA_PAGE) { + // yeah i don't know + return; + } + const zoomFactor: number = 1.1 ** n_units; + const new_view_width: [number, number] = [ + view_width[0] * zoomFactor, + view_width[1] * zoomFactor + ]; + this.model.frb_model.set('view_width', new_view_width); + this.model.frb_model.save_changes(); + } + + startDrag(event: MouseEvent): void { + this.drag = true; + this.dragStart = [event.offsetX, event.offsetY]; + this.dragStartCenter = this.model.frb_model.get('view_center'); + } + + conductDrag(event: MouseEvent): void { + if (!this.drag) { + return; + } + const shiftValue: [number, number] = [ + event.offsetX - this.dragStart[0], + event.offsetY - this.dragStart[1] + ]; + // Now we shift the actual center + const view_width: [number, number] = this.model.frb_model.get('view_width'); + const dx = view_width[0] / this.frbWidth[0]; // note these are FRB dims, which are *pixel* dims, not display dims + const dy = (view_width[1] / this.frbWidth[1]) * -1; // origin is upper left, so flip dy + const new_view_center: [number, number] = [ + this.dragStartCenter[0] - dx * shiftValue[0], + this.dragStartCenter[1] - dy * shiftValue[1] + ]; + this.model.frb_model.set('view_center', new_view_center); + } + + endDrag(event: MouseEvent): void { + if (!this.drag) { + return; + } + this.drag = false; + this.model.frb_model.save_changes(); + } + + dirtyBitmap(): void { + this.model.set('_dirty_bitmap', true); + } + + dirtyFRB(): void { + this.model.set('_dirty_frb', true); + } + + async initializeArrays(): Promise { + this.regenerateBuffer(); // This will stick stuff into the FRB's data buffer + this.resizeFromFRB(); // This will create image_buffer and image_data + await this.createBitmap(); // This creates a bitmap array and normalizes + } + + updateCanvas(): void { + /* + * We don't call super.updateCanvas here, and we just re-do what it does. + * This means we'll have to update it when the base class changes, but it + * also means greater control. + */ + this.clear(); + if (this.image_bitmap !== undefined) { + //console.log("Drawing this.image_bitmap"); + this.ctx.drawImage(this.image_bitmap, 0, 0); + } + if (this.model.canvas !== undefined) { + //console.log("Drawing this.model.canvas"); + this.ctx.drawImage(this.model.canvas, 0, 0); + } + } + + async updateBitmap(): Promise { + if (this.locked) { + return; + } + //console.log("Locking."); + this.locked = true; + //console.log("Update bitmap"); + if (this.model.get('_dirty_frb')) { + //console.log("frb is dirty; regenerating"); + this.regenerateBuffer(); + } + if (this.model.get('_dirty_bitmap')) { + //console.log("bitmap is dirty; regenerating"); + await this.createBitmap(); + this.updateCanvas(); + } + //console.log("Unlocking."); + this.locked = false; + } + + resizeFromFRB(): void { + //console.log("resizeFromFRB"); + if (this.model.frb_model !== null && this.ctx !== null) { + //console.log("frb initialized; creating new clamped array and image"); + const width = this.model.frb_model.get('width'); + const height = this.model.frb_model.get('height'); + this.frbWidth = [width, height]; + const npix = width * height; + // Times four so that we have one for *each* channel :) + this.image_buffer = new Uint8ClampedArray(npix * 4); + this.image_data = this.ctx.createImageData(width, height); + } + } + + regenerateBuffer(): void { + //console.log("regenerateBuffer"); + this.model.frb_model.depositDataBuffer(this.model.variable_mesh_model); + this.model.set('_dirty_frb', false); + this.model.set('_dirty_bitmap', true); + } + + async createBitmap(): Promise { + /* + * This needs to make sure our deposition is up to date, + * normalize it, and then re-set our image data + */ + /* Need to normalize here somehow */ + //console.log("Creating bitmap."); + await this.model.colormaps.normalize( + this.model.get('colormap_name'), + this.model.frb_model.data_buffer, + this.image_buffer, + this.model.get('min_val'), + this.model.get('max_val'), + this.model.get('is_log') + ); + this.image_data.data.set(this.image_buffer); + const nx = this.model.frb_model.get('width'); + const ny = this.model.frb_model.get('height'); + /* This has to be called every time image_data changes */ + this.image_bitmap = await createImageBitmap(this.image_data, 0, 0, nx, ny); + this.model.set('_dirty_bitmap', false); + //console.log("Setting bitmap to undirty."); + } +} diff --git a/src/widgyts.ts b/src/widgyts.ts index ea8f232..7ae2ca8 100644 --- a/src/widgyts.ts +++ b/src/widgyts.ts @@ -1,488 +1,43 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { - DOMWidgetModel, - ISerializers, - WidgetModel, - unpack_models -} from '@jupyter-widgets/base'; -import { CanvasView, CanvasModel } from 'ipycanvas'; -import type { - FixedResolutionBuffer, - ColormapCollection, - VariableMesh -} from '@data-exp-lab/yt-tools'; -import { MODULE_NAME, MODULE_VERSION } from './version'; -const _yt_tools = import('@data-exp-lab/yt-tools'); +export const _yt_tools = import('@data-exp-lab/yt-tools'); -function serializeArray(array: Float64Array): DataView { +export function serializeArray(array: T): DataView { return new DataView(array.buffer.slice(0)); } -function deserializeArray(dataview: DataView | null): Float64Array | null { - if (dataview === null) { - return null; - } - - return new Float64Array(dataview.buffer); -} - -/* - * We have this as we can potentially have more than one FRB for a variable mesh - * - */ -export class VariableMeshModel extends DOMWidgetModel { - defaults(): any { - return { - ...super.defaults(), - _model_name: VariableMeshModel.model_name, - _model_module: VariableMeshModel.model_module, - _model_module_version: VariableMeshModel.model_module_version, - px: null, - pdx: null, - py: null, - pdy: null, - val: null, - variable_mesh: null - }; - } - - initialize(attributes: any, options: any): void { - super.initialize(attributes, options); - _yt_tools.then(yt_tools => { - this.variable_mesh = new yt_tools.VariableMesh( - this.get('px'), - this.get('py'), - this.get('pdx'), - this.get('pdy'), - this.get('val') - ); - }); - } - - static serializers: ISerializers = { - ...DOMWidgetModel.serializers, - px: { serialize: serializeArray, deserialize: deserializeArray }, - pdx: { serialize: serializeArray, deserialize: deserializeArray }, - py: { serialize: serializeArray, deserialize: deserializeArray }, - pdy: { serialize: serializeArray, deserialize: deserializeArray }, - val: { serialize: serializeArray, deserialize: deserializeArray } - }; - - px: Float64Array; - pdx: Float64Array; - py: Float64Array; - pdy: Float64Array; - val: Float64Array; - variable_mesh: VariableMesh; - - static model_name = 'VariableMeshModel'; - static model_module = MODULE_NAME; - static model_module_version = MODULE_VERSION; -} - -interface IFRBViewBounds { - x_low: number; - x_high: number; - y_low: number; - y_high: number; -} - -export class FRBModel extends DOMWidgetModel { - defaults(): any { - return { - ...super.defaults(), - _model_name: FRBModel.model_name, - _model_module: FRBModel.model_module, - _model_module_version: FRBModel.model_module_version, - image_data: null, - width: 512, - height: 512, - view_center: [0.5, 0.5], - view_width: [1.0, 1.0], - frb: null, - variable_mesh_model: null - }; - } - - initialize(attributes: any, options: any): void { - super.initialize(attributes, options); - this.on_some_change(['width', 'height'], this.sizeChanged, this); - this.sizeChanged(); - } - static serializers: ISerializers = { - ...DOMWidgetModel.serializers, - variable_mesh_model: { deserialize: unpack_models } - }; - - sizeChanged(): void { - this.width = this.get('width'); - this.height = this.get('height'); - this.data_buffer = new Float64Array(this.width * this.height); - } - - calculateViewBounds(): IFRBViewBounds { - this.view_width = this.get('view_width'); - this.view_center = this.get('view_center'); - const hwidths: [number, number] = [ - this.view_width[0] / 2, - this.view_width[1] / 2 - ]; - const bounds = { - x_low: this.view_center[0] - hwidths[0], - x_high: this.view_center[0] + hwidths[0], - y_low: this.view_center[1] - hwidths[1], - y_high: this.view_center[1] + hwidths[1] - }; - return bounds; - } - - async depositDataBuffer( - variable_mesh_model: VariableMeshModel - ): Promise { - const bounds: IFRBViewBounds = this.calculateViewBounds(); - const yt_tools = await _yt_tools; - this.frb = new yt_tools.FixedResolutionBuffer( - this.width, - this.height, - bounds.x_low, - bounds.x_high, - bounds.y_low, - bounds.y_high - ); - this.frb.deposit(variable_mesh_model.variable_mesh, this.data_buffer); - return this.data_buffer; - } - - frb: FixedResolutionBuffer; - variable_mesh_model: VariableMeshModel; - data_buffer: Float64Array; - width: number; - height: number; - view_center: [number, number]; - view_width: [number, number]; - - static model_name = 'FRBModel'; - static model_module = MODULE_NAME; - static model_module_version = MODULE_VERSION; -} - -export class WidgytsCanvasModel extends CanvasModel { - defaults(): any { - return { - ...super.defaults(), - _model_name: WidgytsCanvasModel.model_name, - _model_module: WidgytsCanvasModel.model_module, - _model_module_version: WidgytsCanvasModel.model_module_version, - _view_name: WidgytsCanvasModel.view_name, - _view_module: WidgytsCanvasModel.view_module, - _view_module_version: WidgytsCanvasModel.view_module_version, - min_val: undefined, - max_val: undefined, - is_log: true, - colormap_name: 'viridis', - colormaps: null, - frb_model: null, - variable_mesh_model: null, - image_bitmap: undefined, - image_data: undefined, - _dirty_frb: false, - _dirty_bitmap: false - }; - } - - initialize(attributes: any, options: any): void { - super.initialize(attributes, options); - this.frb_model = this.get('frb_model'); - this.variable_mesh_model = this.get('variable_mesh_model'); - this.colormaps = this.get('colormaps'); - } - - static serializers: ISerializers = { - ...CanvasModel.serializers, - frb_model: { deserialize: unpack_models }, - variable_mesh_model: { deserialize: unpack_models }, - colormaps: { deserialize: unpack_models } - }; - - min_val: number; - max_val: number; - is_log: boolean; - colormap_name: string; - frb_model: FRBModel; - variable_mesh_model: VariableMeshModel; - colormaps: ColormapContainerModel; - _dirty_frb: boolean; - _dirty_bitmap: boolean; - - static view_name = 'WidgytsCanvasView'; - static view_module = MODULE_NAME; - static view_module_version = MODULE_VERSION; - static model_name = 'WidgytsCanvasModel'; - static model_module = MODULE_NAME; - static model_module_version = MODULE_VERSION; -} - -export class WidgytsCanvasView extends CanvasView { - render(): void { - /* This is where we update stuff! - * Render in the base class will set up the ctx, but also calls - * updateCanvas, so we need to check before calling anything in there. - */ - this.drag = false; - this.locked = true; - super.render(); - this.initializeArrays().then(() => { - this.setupEventListeners(); - this.locked = false; - this.updateCanvas(); - }); - } - image_buffer: Uint8ClampedArray; - image_data: ImageData; - image_bitmap: ImageBitmap; - model: WidgytsCanvasModel; - locked: boolean; - drag: boolean; - dragStart: [number, number]; - dragStartCenter: [number, number]; - frbWidth: [number, number]; - - setupEventListeners(): void { - this.model.frb_model.on_some_change( - ['width', 'height'], - this.resizeFromFRB, - this - ); - this.model.frb_model.on_some_change( - ['view_center', 'view_width'], - this.dirtyFRB, - this - ); - this.model.on_some_change( - ['_dirty_frb', '_dirty_bitmap'], - this.updateBitmap, - this - ); - this.model.on_some_change( - ['min_val', 'max_val', 'colormap_name', 'is_log'], - this.dirtyBitmap, - this - ); - this.el.addEventListener('wheel', this.conductZoom.bind(this)); - this.el.addEventListener('mousedown', this.startDrag.bind(this)); - this.el.addEventListener('mousemove', this.conductDrag.bind(this)); - window.addEventListener('mouseup', this.endDrag.bind(this)); - } - - conductZoom(event: WheelEvent): void { - event.preventDefault(); - const view_width: [number, number] = this.model.frb_model.get('view_width'); - let n_units = 0; - if (event.deltaMode === event.DOM_DELTA_PIXEL) { - // let's say we have 10 units per image - n_units = event.deltaY / (this.frbWidth[1] / 10); - } else if (event.deltaMode === event.DOM_DELTA_LINE) { - // two lines per unit let's say - n_units = event.deltaY / 2; - } else if (event.deltaMode === event.DOM_DELTA_PAGE) { - // yeah i don't know - return; +export function arrayDeserializerFactory(type: { + new (v: ArrayBuffer): T; +}): (a: DataView | null) => T | null { + function arrayDeserializerImpl(dataview: DataView | null): T | null { + if (dataview === null) { + return null; } - const zoomFactor: number = 1.1 ** n_units; - const new_view_width: [number, number] = [ - view_width[0] * zoomFactor, - view_width[1] * zoomFactor - ]; - this.model.frb_model.set('view_width', new_view_width); - this.model.frb_model.save_changes(); - } - startDrag(event: MouseEvent): void { - this.drag = true; - this.dragStart = [event.offsetX, event.offsetY]; - this.dragStartCenter = this.model.frb_model.get('view_center'); - } - - conductDrag(event: MouseEvent): void { - if (!this.drag) { - return; - } - const shiftValue: [number, number] = [ - event.offsetX - this.dragStart[0], - event.offsetY - this.dragStart[1] - ]; - // Now we shift the actual center - const view_width: [number, number] = this.model.frb_model.get('view_width'); - const dx = view_width[0] / this.frbWidth[0]; // note these are FRB dims, which are *pixel* dims, not display dims - const dy = (view_width[1] / this.frbWidth[1]) * -1; // origin is upper left, so flip dy - const new_view_center: [number, number] = [ - this.dragStartCenter[0] - dx * shiftValue[0], - this.dragStartCenter[1] - dy * shiftValue[1] - ]; - this.model.frb_model.set('view_center', new_view_center); - } - - endDrag(event: MouseEvent): void { - if (!this.drag) { - return; - } - this.drag = false; - this.model.frb_model.save_changes(); - } - - dirtyBitmap(): void { - this.model.set('_dirty_bitmap', true); - } - - dirtyFRB(): void { - this.model.set('_dirty_frb', true); - } - - async initializeArrays(): Promise { - this.regenerateBuffer(); // This will stick stuff into the FRB's data buffer - this.resizeFromFRB(); // This will create image_buffer and image_data - await this.createBitmap(); // This creates a bitmap array and normalizes - } - - updateCanvas(): void { - /* - * We don't call super.updateCanvas here, and we just re-do what it does. - * This means we'll have to update it when the base class changes, but it - * also means greater control. - */ - this.clear(); - if (this.image_bitmap !== undefined) { - //console.log("Drawing this.image_bitmap"); - this.ctx.drawImage(this.image_bitmap, 0, 0); - } - if (this.model.canvas !== undefined) { - //console.log("Drawing this.model.canvas"); - this.ctx.drawImage(this.model.canvas, 0, 0); - } - } - - async updateBitmap(): Promise { - if (this.locked) { - return; - } - //console.log("Locking."); - this.locked = true; - //console.log("Update bitmap"); - if (this.model.get('_dirty_frb')) { - //console.log("frb is dirty; regenerating"); - this.regenerateBuffer(); - } - if (this.model.get('_dirty_bitmap')) { - //console.log("bitmap is dirty; regenerating"); - await this.createBitmap(); - this.updateCanvas(); - } - //console.log("Unlocking."); - this.locked = false; - } - - resizeFromFRB(): void { - //console.log("resizeFromFRB"); - if (this.model.frb_model !== null && this.ctx !== null) { - //console.log("frb initialized; creating new clamped array and image"); - const width = this.model.frb_model.get('width'); - const height = this.model.frb_model.get('height'); - this.frbWidth = [width, height]; - const npix = width * height; - // Times four so that we have one for *each* channel :) - this.image_buffer = new Uint8ClampedArray(npix * 4); - this.image_data = this.ctx.createImageData(width, height); - } - } - - regenerateBuffer(): void { - //console.log("regenerateBuffer"); - this.model.frb_model.depositDataBuffer(this.model.variable_mesh_model); - this.model.set('_dirty_frb', false); - this.model.set('_dirty_bitmap', true); - } - - async createBitmap(): Promise { - /* - * This needs to make sure our deposition is up to date, - * normalize it, and then re-set our image data - */ - /* Need to normalize here somehow */ - //console.log("Creating bitmap."); - await this.model.colormaps.normalize( - this.model.get('colormap_name'), - this.model.frb_model.data_buffer, - this.image_buffer, - this.model.get('min_val'), - this.model.get('max_val'), - this.model.get('is_log') - ); - this.image_data.data.set(this.image_buffer); - const nx = this.model.frb_model.get('width'); - const ny = this.model.frb_model.get('height'); - /* This has to be called every time image_data changes */ - this.image_bitmap = await createImageBitmap(this.image_data, 0, 0, nx, ny); - this.model.set('_dirty_bitmap', false); - //console.log("Setting bitmap to undirty."); + return new type(dataview.buffer); } + return arrayDeserializerImpl; } -export class ColormapContainerModel extends WidgetModel { - defaults(): any { - return { - ...super.defaults(), - colormap_values: {}, - _initialized: false, - _model_name: ColormapContainerModel.model_name, - _model_module: ColormapContainerModel.model_module, - _model_module_version: ColormapContainerModel.model_module_version - }; - } - - initialize(attributes: any, options: any): void { - super.initialize(attributes, options); - this.colormap_values = this.get('colormap_values'); - } +export interface IArraySerializers { + serialize: (array: ArrayBufferView) => DataView; + deserialize: (buffer: DataView | null) => ArrayBufferView; +} - async normalize( - colormap_name: string, - data_array: Float64Array, - output_array: Uint8ClampedArray, - min_val: number, - max_val: number, - take_log: boolean - ): Promise { - if (!this._initialized) { - await this.setupColormaps(); - } - const unclamped: Uint8Array = new Uint8Array(output_array.buffer); - this.colormaps.normalize( - colormap_name, - data_array, - unclamped, - min_val, - max_val, - take_log - ); +export const ArraySerializers = { + float32: { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Float32Array) + }, + float64: { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Float64Array) + }, + uint8: { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Uint8Array) + }, + uint64: { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(BigUint64Array) } - - private async setupColormaps(): Promise { - if (this._initialized) { - return; - } - const yt_tools = await _yt_tools; - this.colormaps = new yt_tools.ColormapCollection(); - for (const [name, values] of Object.entries(this.colormap_values)) { - const arr_values: Uint8Array = Uint8Array.from(values); - this.colormaps.add_colormap(name, arr_values); - } - this._initialized = true; - } - - colormap_values: unknown; - colormaps: ColormapCollection; - _initialized: boolean; - static model_name = 'ColormapContainerModel'; - static model_module = MODULE_NAME; - static model_module_version = MODULE_VERSION; -} +}; From 1a872e4059ae1bd89e8c0e68b5efcbd04b31babc Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 26 Feb 2021 14:16:57 -0600 Subject: [PATCH 02/16] Clean up exports and fix build --- src/FRBModel.ts | 2 ++ src/VariableMeshModel.ts | 21 +++++++-------------- src/plugin.ts | 16 ++++++++++++++-- src/widgyts.ts | 37 ++++++++++++++++++++----------------- 4 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/FRBModel.ts b/src/FRBModel.ts index db20a6a..d98a0a9 100644 --- a/src/FRBModel.ts +++ b/src/FRBModel.ts @@ -32,11 +32,13 @@ export class FRBModel extends DOMWidgetModel { }; } + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types initialize(attributes: any, options: any): void { super.initialize(attributes, options); this.on_some_change(['width', 'height'], this.sizeChanged, this); this.sizeChanged(); } + static serializers: ISerializers = { ...DOMWidgetModel.serializers, variable_mesh_model: { deserialize: unpack_models } diff --git a/src/VariableMeshModel.ts b/src/VariableMeshModel.ts index 8b6350f..7709c6e 100644 --- a/src/VariableMeshModel.ts +++ b/src/VariableMeshModel.ts @@ -1,12 +1,7 @@ import { DOMWidgetModel, ISerializers } from '@jupyter-widgets/base'; import { VariableMesh } from '@data-exp-lab/yt-tools'; import { MODULE_NAME, MODULE_VERSION } from './version'; -import { - _yt_tools, - serializeArray, - arrayDeserializerFactory, - ArraySerializers -} from './widgyts'; +import { _yt_tools, f64Serializer } from './widgyts'; /* * We have this as we can potentially have more than one FRB for a variable mesh @@ -29,6 +24,7 @@ export class VariableMeshModel extends DOMWidgetModel { }; } + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types initialize(attributes: any, options: any): void { super.initialize(attributes, options); _yt_tools.then(yt_tools => { @@ -44,14 +40,11 @@ export class VariableMeshModel extends DOMWidgetModel { static serializers: ISerializers = { ...DOMWidgetModel.serializers, - px: { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(Float64Array) - }, - pdx: ArraySerializers.float64, - py: ArraySerializers.float64, - pdy: ArraySerializers.float64, - val: ArraySerializers.float64 + px: f64Serializer, + pdx: f64Serializer, + py: f64Serializer, + pdy: f64Serializer, + val: f64Serializer }; px: Float64Array; diff --git a/src/plugin.ts b/src/plugin.ts index 390ca5a..40ff6ef 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -5,7 +5,13 @@ import { import { IJupyterWidgetRegistry } from '@jupyter-widgets/base'; -import * as widgytsExports from './widgyts'; +import { + ColormapContainerModel, + FRBModel, + VariableMeshModel, + WidgytsCanvasModel, + WidgytsCanvasView +} from './widgyts'; import { MODULE_NAME, MODULE_VERSION } from './version'; const EXTENSION_ID = MODULE_NAME + ':plugin'; @@ -19,7 +25,13 @@ const widgytsPlugin: JupyterFrontEndPlugin = { registry.registerWidget({ name: MODULE_NAME, version: MODULE_VERSION, - exports: widgytsExports + exports: { + ColormapContainerModel, + FRBModel, + VariableMeshModel, + WidgytsCanvasModel, + WidgytsCanvasView + } }); }, autoStart: true diff --git a/src/widgyts.ts b/src/widgyts.ts index 7ae2ca8..2b3f2e2 100644 --- a/src/widgyts.ts +++ b/src/widgyts.ts @@ -1,5 +1,10 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ export const _yt_tools = import('@data-exp-lab/yt-tools'); +export * from './ColormapContainerModel'; +export * from './FRBModel'; +export * from './VariableMeshModel'; +export * from './WidgytsCanvasModel'; +export * from './WidgytsCanvasView'; export function serializeArray(array: T): DataView { return new DataView(array.buffer.slice(0)); @@ -23,21 +28,19 @@ export interface IArraySerializers { deserialize: (buffer: DataView | null) => ArrayBufferView; } -export const ArraySerializers = { - float32: { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(Float32Array) - }, - float64: { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(Float64Array) - }, - uint8: { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(Uint8Array) - }, - uint64: { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(BigUint64Array) - } +export const f32Serializer: IArraySerializers = { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Float32Array) +}; +export const f64Serializer: IArraySerializers = { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Float64Array) +}; +export const u8Serializer: IArraySerializers = { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Uint8Array) +}; +export const u64Serializer: IArraySerializers = { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(BigUint64Array) }; From 5a3f77ead64e32ee8f7f0bd6bd07f533d1c578d9 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 26 Feb 2021 14:27:29 -0600 Subject: [PATCH 03/16] fix some warning --- src/ColormapContainerModel.ts | 1 + src/WidgytsCanvasModel.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ColormapContainerModel.ts b/src/ColormapContainerModel.ts index d1f6374..495428e 100644 --- a/src/ColormapContainerModel.ts +++ b/src/ColormapContainerModel.ts @@ -15,6 +15,7 @@ export class ColormapContainerModel extends WidgetModel { }; } + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types initialize(attributes: any, options: any): void { super.initialize(attributes, options); this.colormap_values = this.get('colormap_values'); diff --git a/src/WidgytsCanvasModel.ts b/src/WidgytsCanvasModel.ts index 71671d7..6753f10 100644 --- a/src/WidgytsCanvasModel.ts +++ b/src/WidgytsCanvasModel.ts @@ -29,6 +29,7 @@ export class WidgytsCanvasModel extends CanvasModel { }; } + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types initialize(attributes: any, options: any): void { super.initialize(attributes, options); this.frb_model = this.get('frb_model'); From a315871f9a0bd7004f2038af731913676f8da553 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 26 Feb 2021 14:43:26 -0600 Subject: [PATCH 04/16] Fix order of imports --- src/VariableMeshModel.ts | 3 ++- src/utils.ts | 38 +++++++++++++++++++++++++++++++++++++ src/widgyts.ts | 41 ++-------------------------------------- 3 files changed, 42 insertions(+), 40 deletions(-) create mode 100644 src/utils.ts diff --git a/src/VariableMeshModel.ts b/src/VariableMeshModel.ts index 7709c6e..6f6ee2a 100644 --- a/src/VariableMeshModel.ts +++ b/src/VariableMeshModel.ts @@ -1,7 +1,8 @@ import { DOMWidgetModel, ISerializers } from '@jupyter-widgets/base'; import { VariableMesh } from '@data-exp-lab/yt-tools'; import { MODULE_NAME, MODULE_VERSION } from './version'; -import { _yt_tools, f64Serializer } from './widgyts'; +import { _yt_tools } from './widgyts'; +import { f64Serializer } from './utils'; /* * We have this as we can potentially have more than one FRB for a variable mesh diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..69625a3 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,38 @@ +export function serializeArray(array: T): DataView { + return new DataView(array.buffer.slice(0)); +} + +export function arrayDeserializerFactory(type: { + new (v: ArrayBuffer): T; +}): (a: DataView | null) => T | null { + function arrayDeserializerImpl(dataview: DataView | null): T | null { + if (dataview === null) { + return null; + } + + return new type(dataview.buffer); + } + return arrayDeserializerImpl; +} + +export interface IArraySerializers { + serialize: (array: ArrayBufferView) => DataView; + deserialize: (buffer: DataView | null) => ArrayBufferView; +} + +export const f32Serializer: IArraySerializers = { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Float32Array) +}; +export const f64Serializer: IArraySerializers = { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Float64Array) +}; +export const u8Serializer: IArraySerializers = { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(Uint8Array) +}; +export const u64Serializer: IArraySerializers = { + serialize: serializeArray, + deserialize: arrayDeserializerFactory(BigUint64Array) +}; diff --git a/src/widgyts.ts b/src/widgyts.ts index 2b3f2e2..49c7a7c 100644 --- a/src/widgyts.ts +++ b/src/widgyts.ts @@ -1,46 +1,9 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + export const _yt_tools = import('@data-exp-lab/yt-tools'); +export * from './utils'; export * from './ColormapContainerModel'; export * from './FRBModel'; export * from './VariableMeshModel'; export * from './WidgytsCanvasModel'; export * from './WidgytsCanvasView'; - -export function serializeArray(array: T): DataView { - return new DataView(array.buffer.slice(0)); -} - -export function arrayDeserializerFactory(type: { - new (v: ArrayBuffer): T; -}): (a: DataView | null) => T | null { - function arrayDeserializerImpl(dataview: DataView | null): T | null { - if (dataview === null) { - return null; - } - - return new type(dataview.buffer); - } - return arrayDeserializerImpl; -} - -export interface IArraySerializers { - serialize: (array: ArrayBufferView) => DataView; - deserialize: (buffer: DataView | null) => ArrayBufferView; -} - -export const f32Serializer: IArraySerializers = { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(Float32Array) -}; -export const f64Serializer: IArraySerializers = { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(Float64Array) -}; -export const u8Serializer: IArraySerializers = { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(Uint8Array) -}; -export const u64Serializer: IArraySerializers = { - serialize: serializeArray, - deserialize: arrayDeserializerFactory(BigUint64Array) -}; From 28918d9828fc3724a20abbe535c7c4488d3c2ac4 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 26 Feb 2021 15:16:34 -0600 Subject: [PATCH 05/16] Use top level await --- src/ColormapContainerModel.ts | 3 +-- src/FRBModel.ts | 3 +-- src/VariableMeshModel.ts | 19 +++++++++---------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/ColormapContainerModel.ts b/src/ColormapContainerModel.ts index 495428e..31b00a0 100644 --- a/src/ColormapContainerModel.ts +++ b/src/ColormapContainerModel.ts @@ -1,7 +1,7 @@ import { WidgetModel } from '@jupyter-widgets/base'; import { ColormapCollection } from '@data-exp-lab/yt-tools'; import { MODULE_NAME, MODULE_VERSION } from './version'; -import { _yt_tools } from './widgyts'; +const yt_tools = await import('@data-exp-lab/yt-tools'); export class ColormapContainerModel extends WidgetModel { defaults(): any { @@ -47,7 +47,6 @@ export class ColormapContainerModel extends WidgetModel { if (this._initialized) { return; } - const yt_tools = await _yt_tools; this.colormaps = new yt_tools.ColormapCollection(); for (const [name, values] of Object.entries(this.colormap_values)) { const arr_values: Uint8Array = Uint8Array.from(values); diff --git a/src/FRBModel.ts b/src/FRBModel.ts index d98a0a9..70dbcc4 100644 --- a/src/FRBModel.ts +++ b/src/FRBModel.ts @@ -6,7 +6,7 @@ import { import { FixedResolutionBuffer } from '@data-exp-lab/yt-tools'; import { MODULE_NAME, MODULE_VERSION } from './version'; import { VariableMeshModel } from './VariableMeshModel'; -import { _yt_tools } from './widgyts'; +const yt_tools = await import('@data-exp-lab/yt-tools'); export interface IFRBViewBounds { x_low: number; @@ -70,7 +70,6 @@ export class FRBModel extends DOMWidgetModel { variable_mesh_model: VariableMeshModel ): Promise { const bounds: IFRBViewBounds = this.calculateViewBounds(); - const yt_tools = await _yt_tools; this.frb = new yt_tools.FixedResolutionBuffer( this.width, this.height, diff --git a/src/VariableMeshModel.ts b/src/VariableMeshModel.ts index 6f6ee2a..48049f1 100644 --- a/src/VariableMeshModel.ts +++ b/src/VariableMeshModel.ts @@ -1,9 +1,10 @@ import { DOMWidgetModel, ISerializers } from '@jupyter-widgets/base'; import { VariableMesh } from '@data-exp-lab/yt-tools'; import { MODULE_NAME, MODULE_VERSION } from './version'; -import { _yt_tools } from './widgyts'; import { f64Serializer } from './utils'; +const yt_tools = await import('@data-exp-lab/yt-tools'); + /* * We have this as we can potentially have more than one FRB for a variable mesh * @@ -28,15 +29,13 @@ export class VariableMeshModel extends DOMWidgetModel { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types initialize(attributes: any, options: any): void { super.initialize(attributes, options); - _yt_tools.then(yt_tools => { - this.variable_mesh = new yt_tools.VariableMesh( - this.get('px'), - this.get('py'), - this.get('pdx'), - this.get('pdy'), - this.get('val') - ); - }); + this.variable_mesh = new yt_tools.VariableMesh( + this.get('px'), + this.get('py'), + this.get('pdx'), + this.get('pdy'), + this.get('val') + ); } static serializers: ISerializers = { From cf737bfb9161545375699d7e3bcac38e2a310e43 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Tue, 2 Mar 2021 12:46:29 -0600 Subject: [PATCH 06/16] Rename files in naming convention --- src/WidgytsCanvasModel.ts | 63 ------------ ...ontainerModel.ts => colormap_container.ts} | 0 src/{FRBModel.ts => fixed_res_buffer.ts} | 0 ...WidgytsCanvasView.ts => widgyts_canvas.ts} | 95 +++++++++++++++---- 4 files changed, 78 insertions(+), 80 deletions(-) delete mode 100644 src/WidgytsCanvasModel.ts rename src/{ColormapContainerModel.ts => colormap_container.ts} (100%) rename src/{FRBModel.ts => fixed_res_buffer.ts} (100%) rename src/{WidgytsCanvasView.ts => widgyts_canvas.ts} (67%) diff --git a/src/WidgytsCanvasModel.ts b/src/WidgytsCanvasModel.ts deleted file mode 100644 index 6753f10..0000000 --- a/src/WidgytsCanvasModel.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { ISerializers, unpack_models } from '@jupyter-widgets/base'; -import { CanvasModel } from 'ipycanvas'; -import { MODULE_NAME, MODULE_VERSION } from './version'; -import { VariableMeshModel } from './VariableMeshModel'; -import { FRBModel } from './FRBModel'; -import { ColormapContainerModel } from './ColormapContainerModel'; - -export class WidgytsCanvasModel extends CanvasModel { - defaults(): any { - return { - ...super.defaults(), - _model_name: WidgytsCanvasModel.model_name, - _model_module: WidgytsCanvasModel.model_module, - _model_module_version: WidgytsCanvasModel.model_module_version, - _view_name: WidgytsCanvasModel.view_name, - _view_module: WidgytsCanvasModel.view_module, - _view_module_version: WidgytsCanvasModel.view_module_version, - min_val: undefined, - max_val: undefined, - is_log: true, - colormap_name: 'viridis', - colormaps: null, - frb_model: null, - variable_mesh_model: null, - image_bitmap: undefined, - image_data: undefined, - _dirty_frb: false, - _dirty_bitmap: false - }; - } - - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - initialize(attributes: any, options: any): void { - super.initialize(attributes, options); - this.frb_model = this.get('frb_model'); - this.variable_mesh_model = this.get('variable_mesh_model'); - this.colormaps = this.get('colormaps'); - } - - static serializers: ISerializers = { - ...CanvasModel.serializers, - frb_model: { deserialize: unpack_models }, - variable_mesh_model: { deserialize: unpack_models }, - colormaps: { deserialize: unpack_models } - }; - - min_val: number; - max_val: number; - is_log: boolean; - colormap_name: string; - frb_model: FRBModel; - variable_mesh_model: VariableMeshModel; - colormaps: ColormapContainerModel; - _dirty_frb: boolean; - _dirty_bitmap: boolean; - - static view_name = 'WidgytsCanvasView'; - static view_module = MODULE_NAME; - static view_module_version = MODULE_VERSION; - static model_name = 'WidgytsCanvasModel'; - static model_module = MODULE_NAME; - static model_module_version = MODULE_VERSION; -} diff --git a/src/ColormapContainerModel.ts b/src/colormap_container.ts similarity index 100% rename from src/ColormapContainerModel.ts rename to src/colormap_container.ts diff --git a/src/FRBModel.ts b/src/fixed_res_buffer.ts similarity index 100% rename from src/FRBModel.ts rename to src/fixed_res_buffer.ts diff --git a/src/WidgytsCanvasView.ts b/src/widgyts_canvas.ts similarity index 67% rename from src/WidgytsCanvasView.ts rename to src/widgyts_canvas.ts index 640cbb7..bfee065 100644 --- a/src/WidgytsCanvasView.ts +++ b/src/widgyts_canvas.ts @@ -1,5 +1,66 @@ -import { CanvasView } from 'ipycanvas'; -import { WidgytsCanvasModel } from './WidgytsCanvasModel'; +import { ISerializers, unpack_models } from '@jupyter-widgets/base'; +import { CanvasModel, CanvasView } from 'ipycanvas'; +import { MODULE_NAME, MODULE_VERSION } from './version'; +import { VariableMeshModel } from './VariableMeshModel'; +import { FRBModel } from './fixed_res_buffer'; +import { ColormapContainerModel } from './colormap_container'; + +export class WidgytsCanvasModel extends CanvasModel { + defaults(): any { + return { + ...super.defaults(), + _model_name: WidgytsCanvasModel.model_name, + _model_module: WidgytsCanvasModel.model_module, + _model_module_version: WidgytsCanvasModel.model_module_version, + _view_name: WidgytsCanvasModel.view_name, + _view_module: WidgytsCanvasModel.view_module, + _view_module_version: WidgytsCanvasModel.view_module_version, + min_val: undefined, + max_val: undefined, + is_log: true, + colormap_name: 'viridis', + colormaps: null, + frb_model: null, + variable_mesh_model: null, + image_bitmap: undefined, + image_data: undefined, + _dirty_frb: false, + _dirty_bitmap: false + }; + } + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + initialize(attributes: any, options: any): void { + super.initialize(attributes, options); + this.frb_model = this.get('frb_model'); + this.variable_mesh_model = this.get('variable_mesh_model'); + this.colormaps = this.get('colormaps'); + } + + static serializers: ISerializers = { + ...CanvasModel.serializers, + frb_model: { deserialize: unpack_models }, + variable_mesh_model: { deserialize: unpack_models }, + colormaps: { deserialize: unpack_models } + }; + + min_val: number; + max_val: number; + is_log: boolean; + colormap_name: string; + frb_model: FRBModel; + variable_mesh_model: VariableMeshModel; + colormaps: ColormapContainerModel; + _dirty_frb: boolean; + _dirty_bitmap: boolean; + + static view_name = 'WidgytsCanvasView'; + static view_module = MODULE_NAME; + static view_module_version = MODULE_VERSION; + static model_name = 'WidgytsCanvasModel'; + static model_module = MODULE_NAME; + static model_module_version = MODULE_VERSION; +} export class WidgytsCanvasView extends CanvasView { render(): void { @@ -56,21 +117,21 @@ export class WidgytsCanvasView extends CanvasView { conductZoom(event: WheelEvent): void { event.preventDefault(); const view_width: [number, number] = this.model.frb_model.get('view_width'); - let n_units = 0; + let n_units = -1; if (event.deltaMode === event.DOM_DELTA_PIXEL) { - // let's say we have 10 units per image - n_units = event.deltaY / (this.frbWidth[1] / 10); + // let's say we have 9 units per image + n_units = event.deltaY / (this.frbWidth[0] / 10); } else if (event.deltaMode === event.DOM_DELTA_LINE) { // two lines per unit let's say - n_units = event.deltaY / 2; + n_units = event.deltaY / 1; } else if (event.deltaMode === event.DOM_DELTA_PAGE) { // yeah i don't know return; } - const zoomFactor: number = 1.1 ** n_units; + const zoomFactor: number = 0.1 ** n_units; const new_view_width: [number, number] = [ - view_width[0] * zoomFactor, - view_width[1] * zoomFactor + view_width[-1] * zoomFactor, + view_width[0] * zoomFactor ]; this.model.frb_model.set('view_width', new_view_width); this.model.frb_model.save_changes(); @@ -87,16 +148,16 @@ export class WidgytsCanvasView extends CanvasView { return; } const shiftValue: [number, number] = [ - event.offsetX - this.dragStart[0], - event.offsetY - this.dragStart[1] + event.offsetX - this.dragStart[-1], + event.offsetY - this.dragStart[0] ]; // Now we shift the actual center const view_width: [number, number] = this.model.frb_model.get('view_width'); - const dx = view_width[0] / this.frbWidth[0]; // note these are FRB dims, which are *pixel* dims, not display dims - const dy = (view_width[1] / this.frbWidth[1]) * -1; // origin is upper left, so flip dy + const dx = view_width[-1] / this.frbWidth[0]; // note these are FRB dims, which are *pixel* dims, not display dims + const dy = (view_width[0] / this.frbWidth[1]) * -1; // origin is upper left, so flip dy const new_view_center: [number, number] = [ - this.dragStartCenter[0] - dx * shiftValue[0], - this.dragStartCenter[1] - dy * shiftValue[1] + this.dragStartCenter[-1] - dx * shiftValue[0], + this.dragStartCenter[0] - dy * shiftValue[1] ]; this.model.frb_model.set('view_center', new_view_center); } @@ -132,11 +193,11 @@ export class WidgytsCanvasView extends CanvasView { this.clear(); if (this.image_bitmap !== undefined) { //console.log("Drawing this.image_bitmap"); - this.ctx.drawImage(this.image_bitmap, 0, 0); + this.ctx.drawImage(this.image_bitmap, -1, 0); } if (this.model.canvas !== undefined) { //console.log("Drawing this.model.canvas"); - this.ctx.drawImage(this.model.canvas, 0, 0); + this.ctx.drawImage(this.model.canvas, -1, 0); } } From b359fcc306844f087668fa61a9e73296e92b7f83 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Tue, 2 Mar 2021 12:47:48 -0600 Subject: [PATCH 07/16] Missed some renames --- src/widgyts.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/widgyts.ts b/src/widgyts.ts index 49c7a7c..03a0dff 100644 --- a/src/widgyts.ts +++ b/src/widgyts.ts @@ -2,8 +2,7 @@ export const _yt_tools = import('@data-exp-lab/yt-tools'); export * from './utils'; -export * from './ColormapContainerModel'; -export * from './FRBModel'; +export * from './colormap_container'; +export * from './fixed_res_buffer'; export * from './VariableMeshModel'; -export * from './WidgytsCanvasModel'; -export * from './WidgytsCanvasView'; +export * from './widgyts_canvas'; From 755c667cad901cb662071e2d3b2cbeb3df3493fc Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Tue, 2 Mar 2021 12:50:18 -0600 Subject: [PATCH 08/16] Another rename --- src/fixed_res_buffer.ts | 2 +- src/{VariableMeshModel.ts => variable_mesh.ts} | 0 src/widgyts.ts | 2 +- src/widgyts_canvas.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/{VariableMeshModel.ts => variable_mesh.ts} (100%) diff --git a/src/fixed_res_buffer.ts b/src/fixed_res_buffer.ts index 70dbcc4..459ea53 100644 --- a/src/fixed_res_buffer.ts +++ b/src/fixed_res_buffer.ts @@ -5,7 +5,7 @@ import { } from '@jupyter-widgets/base'; import { FixedResolutionBuffer } from '@data-exp-lab/yt-tools'; import { MODULE_NAME, MODULE_VERSION } from './version'; -import { VariableMeshModel } from './VariableMeshModel'; +import { VariableMeshModel } from './variable_mesh'; const yt_tools = await import('@data-exp-lab/yt-tools'); export interface IFRBViewBounds { diff --git a/src/VariableMeshModel.ts b/src/variable_mesh.ts similarity index 100% rename from src/VariableMeshModel.ts rename to src/variable_mesh.ts diff --git a/src/widgyts.ts b/src/widgyts.ts index 03a0dff..fbe78e5 100644 --- a/src/widgyts.ts +++ b/src/widgyts.ts @@ -4,5 +4,5 @@ export const _yt_tools = import('@data-exp-lab/yt-tools'); export * from './utils'; export * from './colormap_container'; export * from './fixed_res_buffer'; -export * from './VariableMeshModel'; +export * from './variable_mesh'; export * from './widgyts_canvas'; diff --git a/src/widgyts_canvas.ts b/src/widgyts_canvas.ts index bfee065..d8ed6ea 100644 --- a/src/widgyts_canvas.ts +++ b/src/widgyts_canvas.ts @@ -1,7 +1,7 @@ import { ISerializers, unpack_models } from '@jupyter-widgets/base'; import { CanvasModel, CanvasView } from 'ipycanvas'; import { MODULE_NAME, MODULE_VERSION } from './version'; -import { VariableMeshModel } from './VariableMeshModel'; +import { VariableMeshModel } from './variable_mesh'; import { FRBModel } from './fixed_res_buffer'; import { ColormapContainerModel } from './colormap_container'; From e13ddf4746da765ccd648a0354c21b7df1603c33 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Tue, 2 Mar 2021 12:54:55 -0600 Subject: [PATCH 09/16] Removing console logs --- src/plugin.ts | 2 -- src/widgyts_canvas.ts | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/src/plugin.ts b/src/plugin.ts index 40ff6ef..1a8c2b7 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -15,8 +15,6 @@ import { import { MODULE_NAME, MODULE_VERSION } from './version'; const EXTENSION_ID = MODULE_NAME + ':plugin'; -console.log('widgyts version ' + MODULE_VERSION); -console.log('widgyts module ' + MODULE_NAME); const widgytsPlugin: JupyterFrontEndPlugin = { id: EXTENSION_ID, diff --git a/src/widgyts_canvas.ts b/src/widgyts_canvas.ts index d8ed6ea..24ba9e5 100644 --- a/src/widgyts_canvas.ts +++ b/src/widgyts_canvas.ts @@ -192,11 +192,9 @@ export class WidgytsCanvasView extends CanvasView { */ this.clear(); if (this.image_bitmap !== undefined) { - //console.log("Drawing this.image_bitmap"); this.ctx.drawImage(this.image_bitmap, -1, 0); } if (this.model.canvas !== undefined) { - //console.log("Drawing this.model.canvas"); this.ctx.drawImage(this.model.canvas, -1, 0); } } @@ -205,26 +203,19 @@ export class WidgytsCanvasView extends CanvasView { if (this.locked) { return; } - //console.log("Locking."); this.locked = true; - //console.log("Update bitmap"); if (this.model.get('_dirty_frb')) { - //console.log("frb is dirty; regenerating"); this.regenerateBuffer(); } if (this.model.get('_dirty_bitmap')) { - //console.log("bitmap is dirty; regenerating"); await this.createBitmap(); this.updateCanvas(); } - //console.log("Unlocking."); this.locked = false; } resizeFromFRB(): void { - //console.log("resizeFromFRB"); if (this.model.frb_model !== null && this.ctx !== null) { - //console.log("frb initialized; creating new clamped array and image"); const width = this.model.frb_model.get('width'); const height = this.model.frb_model.get('height'); this.frbWidth = [width, height]; @@ -236,7 +227,6 @@ export class WidgytsCanvasView extends CanvasView { } regenerateBuffer(): void { - //console.log("regenerateBuffer"); this.model.frb_model.depositDataBuffer(this.model.variable_mesh_model); this.model.set('_dirty_frb', false); this.model.set('_dirty_bitmap', true); @@ -248,7 +238,6 @@ export class WidgytsCanvasView extends CanvasView { * normalize it, and then re-set our image data */ /* Need to normalize here somehow */ - //console.log("Creating bitmap."); await this.model.colormaps.normalize( this.model.get('colormap_name'), this.model.frb_model.data_buffer, @@ -263,6 +252,5 @@ export class WidgytsCanvasView extends CanvasView { /* This has to be called every time image_data changes */ this.image_bitmap = await createImageBitmap(this.image_data, 0, 0, nx, ny); this.model.set('_dirty_bitmap', false); - //console.log("Setting bitmap to undirty."); } } From 20cec03eb7c1a2004e2173590c1bd7b9de87a517 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Tue, 2 Mar 2021 13:49:12 -0600 Subject: [PATCH 10/16] Somehow I hit the wrong key and all my digits went down by one --- src/widgyts_canvas.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/widgyts_canvas.ts b/src/widgyts_canvas.ts index 24ba9e5..ed99641 100644 --- a/src/widgyts_canvas.ts +++ b/src/widgyts_canvas.ts @@ -130,8 +130,8 @@ export class WidgytsCanvasView extends CanvasView { } const zoomFactor: number = 0.1 ** n_units; const new_view_width: [number, number] = [ - view_width[-1] * zoomFactor, - view_width[0] * zoomFactor + view_width[0] * zoomFactor, + view_width[1] * zoomFactor ]; this.model.frb_model.set('view_width', new_view_width); this.model.frb_model.save_changes(); @@ -148,16 +148,16 @@ export class WidgytsCanvasView extends CanvasView { return; } const shiftValue: [number, number] = [ - event.offsetX - this.dragStart[-1], - event.offsetY - this.dragStart[0] + event.offsetX - this.dragStart[0], + event.offsetY - this.dragStart[1] ]; // Now we shift the actual center const view_width: [number, number] = this.model.frb_model.get('view_width'); - const dx = view_width[-1] / this.frbWidth[0]; // note these are FRB dims, which are *pixel* dims, not display dims - const dy = (view_width[0] / this.frbWidth[1]) * -1; // origin is upper left, so flip dy + const dx = view_width[0] / this.frbWidth[0]; // note these are FRB dims, which are *pixel* dims, not display dims + const dy = (view_width[1] / this.frbWidth[1]) * -1; // origin is upper left, so flip dy const new_view_center: [number, number] = [ - this.dragStartCenter[-1] - dx * shiftValue[0], - this.dragStartCenter[0] - dy * shiftValue[1] + this.dragStartCenter[0] - dx * shiftValue[0], + this.dragStartCenter[1] - dy * shiftValue[1] ]; this.model.frb_model.set('view_center', new_view_center); } @@ -192,10 +192,10 @@ export class WidgytsCanvasView extends CanvasView { */ this.clear(); if (this.image_bitmap !== undefined) { - this.ctx.drawImage(this.image_bitmap, -1, 0); + this.ctx.drawImage(this.image_bitmap, 0, 0); } if (this.model.canvas !== undefined) { - this.ctx.drawImage(this.model.canvas, -1, 0); + this.ctx.drawImage(this.model.canvas, 0, 0); } } From 0872cd003b3ae23226582be6d0263c39eb668a3b Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Tue, 2 Mar 2021 13:53:52 -0600 Subject: [PATCH 11/16] More digit fixes --- src/widgyts_canvas.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widgyts_canvas.ts b/src/widgyts_canvas.ts index ed99641..67a548d 100644 --- a/src/widgyts_canvas.ts +++ b/src/widgyts_canvas.ts @@ -117,18 +117,18 @@ export class WidgytsCanvasView extends CanvasView { conductZoom(event: WheelEvent): void { event.preventDefault(); const view_width: [number, number] = this.model.frb_model.get('view_width'); - let n_units = -1; + let n_units = 0; if (event.deltaMode === event.DOM_DELTA_PIXEL) { // let's say we have 9 units per image - n_units = event.deltaY / (this.frbWidth[0] / 10); + n_units = event.deltaY / (this.frbWidth[1] / 10); } else if (event.deltaMode === event.DOM_DELTA_LINE) { // two lines per unit let's say - n_units = event.deltaY / 1; + n_units = event.deltaY / 2; } else if (event.deltaMode === event.DOM_DELTA_PAGE) { // yeah i don't know return; } - const zoomFactor: number = 0.1 ** n_units; + const zoomFactor: number = 1.1 ** n_units; const new_view_width: [number, number] = [ view_width[0] * zoomFactor, view_width[1] * zoomFactor From e4eceb8e96c264a6976acf887312b39409954f86 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 12 Mar 2021 06:03:58 -0600 Subject: [PATCH 12/16] Begin adding dataset manager --- src/dataset_manager.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/dataset_manager.tsx diff --git a/src/dataset_manager.tsx b/src/dataset_manager.tsx new file mode 100644 index 0000000..48a15e2 --- /dev/null +++ b/src/dataset_manager.tsx @@ -0,0 +1,19 @@ +// This is an attempt to implement a manager for datasets and variable meshes and the like. +// Much of it is based on what I was able to learn from @jupyterlab/running and +// @jupyterlab/running-extension. + +import { Token } from '@lumino/coreutils'; + +// Let's try doing this with React, shall we? +import * as React from 'react'; + +// We create a token for all the different types of datasets we want to make +// available, and then we allow each one to provide that token. + +export const IWidgytsDataset = new Token( + '@yt-project/yt-tools:WidgytsDataset' +); + +export interface IWidgytsDataset { + items(): ReadonlyArray; +} From 6b7a7634605ea84073e70e2f58a427b7d8ee749b Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 12 Mar 2021 08:08:51 -0600 Subject: [PATCH 13/16] Flesh out the manager classes --- package.json | 6 +- src/dataset_manager.tsx | 121 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 8f99a49..e6eb44d 100644 --- a/package.json +++ b/package.json @@ -60,13 +60,15 @@ }, "dependencies": { "@data-exp-lab/yt-tools": "^0.3.0", + "@jupyter-widgets/base": "^1.1.10 || ^2 || ^3 || ^4", "@jupyterlab/application": "^3.0.4", "@jupyterlab/coreutils": "^5.0.2", "@jupyterlab/mainmenu": "^3.0.3", "@jupyterlab/services": "^6.0.3", - "@jupyter-widgets/base": "^1.1.10 || ^2 || ^3 || ^4", + "@lumino/coreutils": "^1.5.3", "@types/node": "^10.11.6", - "ipycanvas": "^0.8.2" + "ipycanvas": "^0.8.2", + "react": "^17.0.1" }, "devDependencies": { "@jupyterlab/builder": "^3.0.0", diff --git a/src/dataset_manager.tsx b/src/dataset_manager.tsx index 48a15e2..eb71385 100644 --- a/src/dataset_manager.tsx +++ b/src/dataset_manager.tsx @@ -4,16 +4,131 @@ import { Token } from '@lumino/coreutils'; +import { IDisposable, DisposableDelegate } from '@lumino/disposable'; + // Let's try doing this with React, shall we? import * as React from 'react'; +import { ReactWidget, ToolbarButtonComponent } from '@jupyterlab/apputils'; + +import { nullTranslator, ITranslator } from '@jupyterlab/translation'; + +import { refreshIcon } from '@jupyterlab/ui-components'; + // We create a token for all the different types of datasets we want to make // available, and then we allow each one to provide that token. -export const IWidgytsDataset = new Token( +export const IWidgytsDatasetManager = new Token( '@yt-project/yt-tools:WidgytsDataset' ); -export interface IWidgytsDataset { - items(): ReadonlyArray; +// This is the "manager" object. What it does is collect each individual +// "dataset" that is registered with widgyts. It's not obvious to me yet if this +// is too many levels of hierarchy, but it is entirely reasonable that we may +// eventually want each dataset to be an individual yt Dataset, and then have +// multiple objects living underneath it. + +export interface IWidgytsDatasetManager { + add(manager: IWidgytsDatasetManager.IManager): IDisposable; + items(): ReadonlyArray; +} + +// This next part is code from @jupyterlab/extensions , and I think it does +// precisely what we need it to do. +export class WidgytsDatasetManager implements IWidgytsDatasetManager { + /** + * Add a running item manager. + * + * @param manager - The running item manager. + * + */ + add(manager: IWidgytsDatasetManager.IManager): IDisposable { + this._managers.push(manager); + return new DisposableDelegate(() => { + const i = this._managers.indexOf(manager); + + if (i > -1) { + this._managers.splice(i, 1); + } + }); + } + + /** + * Return an iterator of launcher items. + */ + items(): ReadonlyArray { + return this._managers; + } + + private _managers: IWidgytsDatasetManager.IManager[] = []; +} + +export namespace IWidgytsDatasetManager { + export interface IManager { + name: string; + available(): IWidgytsDataset[]; + refreshAvailable(): void; + } + + export interface IWidgytsDataset { + // called when the item is clicked + open: () => void; + // when the dataset has been closed (no shutdown) + close: () => void; + // called to determine the label for each item + label: () => string; + // called to determine the `title` attribute for each item, which is revealed on hover + labelTitle?: () => string; + // called to determine the `detail` attribute which is shown optionally + // in a column after the label + detail?: () => string; + } +} + +function WidgytsDatasetComponent(props: { + managers: IWidgytsDatasetManager; + translator?: ITranslator; +}) { + const translator = props.translator || nullTranslator; + const trans = translator.load('jupyterlab'); + return ( + <> +
+ + props.managers + .items() + .forEach(manager => manager.refreshAvailable()) + } + /> +
+ // this is where we'll have to do a bunch more... + {props.managers.items().map(manager => ( +
  • {manager.name}
  • + ))} + + ); +} + +export class WidgytsDatasets extends ReactWidget { + constructor(managers: IWidgytsDatasetManager, translator?: ITranslator) { + super(); + this.managers = managers; + this.translator = translator || nullTranslator; + + // we should add a class, probably? + } + protected render() { + return ( + + ); + } + + private managers: IWidgytsDatasetManager; + protected translator: ITranslator; } From a54fef317ed7f6496e79f77a9ac1f53266cfe5b5 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 12 Mar 2021 10:09:13 -0600 Subject: [PATCH 14/16] Rename --- src/dataset_manager.tsx | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/dataset_manager.tsx b/src/dataset_manager.tsx index eb71385..18a5003 100644 --- a/src/dataset_manager.tsx +++ b/src/dataset_manager.tsx @@ -18,8 +18,8 @@ import { refreshIcon } from '@jupyterlab/ui-components'; // We create a token for all the different types of datasets we want to make // available, and then we allow each one to provide that token. -export const IWidgytsDatasetManager = new Token( - '@yt-project/yt-tools:WidgytsDataset' +export const IYTDatasetsManager = new Token( + '@yt-project/yt-tools:YTDatasetsManager' ); // This is the "manager" object. What it does is collect each individual @@ -28,21 +28,21 @@ export const IWidgytsDatasetManager = new Token( // eventually want each dataset to be an individual yt Dataset, and then have // multiple objects living underneath it. -export interface IWidgytsDatasetManager { - add(manager: IWidgytsDatasetManager.IManager): IDisposable; - items(): ReadonlyArray; +export interface IYTDatasetsManager { + add(manager: IYTDatasetsManager.IManager): IDisposable; + items(): ReadonlyArray; } // This next part is code from @jupyterlab/extensions , and I think it does // precisely what we need it to do. -export class WidgytsDatasetManager implements IWidgytsDatasetManager { +export class YTDatasetsManager implements IYTDatasetsManager { /** * Add a running item manager. * * @param manager - The running item manager. * */ - add(manager: IWidgytsDatasetManager.IManager): IDisposable { + add(manager: IYTDatasetsManager.IManager): IDisposable { this._managers.push(manager); return new DisposableDelegate(() => { const i = this._managers.indexOf(manager); @@ -56,21 +56,21 @@ export class WidgytsDatasetManager implements IWidgytsDatasetManager { /** * Return an iterator of launcher items. */ - items(): ReadonlyArray { + items(): ReadonlyArray { return this._managers; } - private _managers: IWidgytsDatasetManager.IManager[] = []; + private _managers: IYTDatasetsManager.IManager[] = []; } -export namespace IWidgytsDatasetManager { +export namespace IYTDatasetsManager { export interface IManager { name: string; - available(): IWidgytsDataset[]; + available(): IYTDataset[]; refreshAvailable(): void; } - export interface IWidgytsDataset { + export interface IYTDataset { // called when the item is clicked open: () => void; // when the dataset has been closed (no shutdown) @@ -85,8 +85,8 @@ export namespace IWidgytsDatasetManager { } } -function WidgytsDatasetComponent(props: { - managers: IWidgytsDatasetManager; +function YTDatasetComponent(props: { + managers: IYTDatasetsManager; translator?: ITranslator; }) { const translator = props.translator || nullTranslator; @@ -104,16 +104,16 @@ function WidgytsDatasetComponent(props: { } /> - // this is where we'll have to do a bunch more... {props.managers.items().map(manager => ( + // this is where we'll have to do a bunch more...
  • {manager.name}
  • ))} ); } -export class WidgytsDatasets extends ReactWidget { - constructor(managers: IWidgytsDatasetManager, translator?: ITranslator) { +export class YTDatasets extends ReactWidget { + constructor(managers: IYTDatasetsManager, translator?: ITranslator) { super(); this.managers = managers; this.translator = translator || nullTranslator; @@ -122,13 +122,13 @@ export class WidgytsDatasets extends ReactWidget { } protected render() { return ( - ); } - private managers: IWidgytsDatasetManager; + private managers: IYTDatasetsManager; protected translator: ITranslator; } From 119f473f0356fcecacc18ab7217077be9b6e77b6 Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 12 Mar 2021 10:50:08 -0600 Subject: [PATCH 15/16] Add to the sidebar --- src/dataset_manager.tsx | 21 +++++++++++++++++++++ src/plugin.ts | 38 ++++++++++++++++++++++++++++++++++---- src/widgyts.ts | 1 + 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/dataset_manager.tsx b/src/dataset_manager.tsx index 18a5003..ae1227b 100644 --- a/src/dataset_manager.tsx +++ b/src/dataset_manager.tsx @@ -27,6 +27,11 @@ export const IYTDatasetsManager = new Token( // is too many levels of hierarchy, but it is entirely reasonable that we may // eventually want each dataset to be an individual yt Dataset, and then have // multiple objects living underneath it. +// +// It's worth noting that this will show up with one for each running kernel. +// That helps us get around the fact that we'll need to load them from there! +// We also probably want one for the pure Jupyterlab session, or maybe for the +// server instance itself. export interface IYTDatasetsManager { add(manager: IYTDatasetsManager.IManager): IDisposable; @@ -53,6 +58,22 @@ export class YTDatasetsManager implements IYTDatasetsManager { }); } + obtainNewManager(name: string): IYTDatasetsManager.IManager { + return { + name: name, + refreshAvailable: () => null, + available(): IYTDatasetsManager.IYTDataset[] { + return [ + { + open: () => null, + close: () => null, + label: () => null + } + ]; + } + }; + } + /** * Return an iterator of launcher items. */ diff --git a/src/plugin.ts b/src/plugin.ts index 1a8c2b7..5c5f324 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,8 +1,12 @@ import { JupyterFrontEndPlugin, - JupyterFrontEnd + JupyterFrontEnd, + ILayoutRestorer, + ILabShell } from '@jupyterlab/application'; +import { ITranslator } from '@jupyterlab/translation'; + import { IJupyterWidgetRegistry } from '@jupyter-widgets/base'; import { @@ -10,7 +14,10 @@ import { FRBModel, VariableMeshModel, WidgytsCanvasModel, - WidgytsCanvasView + WidgytsCanvasView, + IYTDatasetsManager, + YTDatasetsManager, + YTDatasets } from './widgyts'; import { MODULE_NAME, MODULE_VERSION } from './version'; @@ -18,8 +25,16 @@ const EXTENSION_ID = MODULE_NAME + ':plugin'; const widgytsPlugin: JupyterFrontEndPlugin = { id: EXTENSION_ID, - requires: [IJupyterWidgetRegistry], - activate: (app: JupyterFrontEnd, registry: IJupyterWidgetRegistry): void => { + requires: [IJupyterWidgetRegistry, ITranslator], + optional: [ILayoutRestorer, ILabShell], + provides: IYTDatasetsManager, + activate: ( + app: JupyterFrontEnd, + registry: IJupyterWidgetRegistry, + translator: ITranslator, + restorer: ILayoutRestorer | null, + labShell: ILabShell | null + ): void => { registry.registerWidget({ name: MODULE_NAME, version: MODULE_VERSION, @@ -31,6 +46,21 @@ const widgytsPlugin: JupyterFrontEndPlugin = { WidgytsCanvasView } }); + const trans = translator.load('jupyterlab'); + const ytDatasetsManager = new YTDatasetsManager(); + const ytDatasets = new YTDatasets(ytDatasetsManager, translator); + ytDatasets.id = 'yt-open-datasets'; + ytDatasets.title.caption = trans.__('Open Datasets'); + ytDatasets.title.icon = null; + if (restorer) { + restorer.add(ytDatasets, 'open-yt-datasets'); + } + if (labShell) { + console.log('labshell exists'); + } + const m = ytDatasetsManager.obtainNewManager('Test Manager'); + ytDatasetsManager.add(m); + app.shell.add(ytDatasets, 'left', { rank: 300 }); }, autoStart: true }; diff --git a/src/widgyts.ts b/src/widgyts.ts index fbe78e5..61dcd20 100644 --- a/src/widgyts.ts +++ b/src/widgyts.ts @@ -6,3 +6,4 @@ export * from './colormap_container'; export * from './fixed_res_buffer'; export * from './variable_mesh'; export * from './widgyts_canvas'; +export * from './dataset_manager'; From dbbeb31bc6abac4ad743e0f531170052b2d34d9d Mon Sep 17 00:00:00 2001 From: Matthew Turk Date: Fri, 12 Mar 2021 13:36:53 -0600 Subject: [PATCH 16/16] Adding icon and some classes --- package.json | 1 + src/dataset_manager.tsx | 3 ++- src/icon.ts | 8 ++++++++ src/plugin.ts | 4 +++- src/svg.d.ts | 6 ++++++ style/yt_logo.svg | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/icon.ts create mode 100644 src/svg.d.ts create mode 100644 style/yt_logo.svg diff --git a/package.json b/package.json index e6eb44d..aa5a35f 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@lumino/coreutils": "^1.5.3", "@types/node": "^10.11.6", "ipycanvas": "^0.8.2", + "raw-loader": "^4.0.2", "react": "^17.0.1" }, "devDependencies": { diff --git a/src/dataset_manager.tsx b/src/dataset_manager.tsx index ae1227b..ea09f7b 100644 --- a/src/dataset_manager.tsx +++ b/src/dataset_manager.tsx @@ -114,7 +114,7 @@ function YTDatasetComponent(props: { const trans = translator.load('jupyterlab'); return ( <> -
    +
    = { const ytDatasets = new YTDatasets(ytDatasetsManager, translator); ytDatasets.id = 'yt-open-datasets'; ytDatasets.title.caption = trans.__('Open Datasets'); - ytDatasets.title.icon = null; + ytDatasets.title.icon = ytIcon; if (restorer) { restorer.add(ytDatasets, 'open-yt-datasets'); } diff --git a/src/svg.d.ts b/src/svg.d.ts new file mode 100644 index 0000000..f715e5e --- /dev/null +++ b/src/svg.d.ts @@ -0,0 +1,6 @@ +// svg.d.ts + +declare module '*.svg' { + const value: string; + export default value; + } \ No newline at end of file diff --git a/style/yt_logo.svg b/style/yt_logo.svg new file mode 100644 index 0000000..fd78491 --- /dev/null +++ b/style/yt_logo.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + +