diff --git a/.circleci/config.yml b/.circleci/config.yml index ad3bd60..6a77361 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -38,8 +38,8 @@ workflows: visual-tests: jobs: - visual: - name: << matrix.jobname >> - matrix: - parameters: - jobname: - - "py311-visual" + name: py311-bqplot012-visual + jobname: py311-bqplot012-visual + - visual: + name: py311-bqplot013-visual + jobname: py311-bqplot013-visual diff --git a/js/lib/BrushEllipseSelector.js b/js/lib/BrushEllipseSelector.js index 5620930..24aaee3 100644 --- a/js/lib/BrushEllipseSelector.js +++ b/js/lib/BrushEllipseSelector.js @@ -11,7 +11,7 @@ const BaseXYSelector = __importStar(require("bqplot")).BaseXYSelector; const d3 = require("d3"); const d3_drag_1 = require("d3-drag"); const d3Selection = require("d3-selection"); -const { applyStyles } = require("./utils"); +const { applyStyles, setRange } = require("./utils"); const d3GetEvent = function () { return require("d3-selection").event; }.bind(this); /* good resource: https://en.wikipedia.org/wiki/Ellipse @@ -198,8 +198,8 @@ class BrushEllipseSelector extends BaseXYSelector { } relayout() { super.relayout(); - this.x_scale.set_range(this.parent.padded_range("x", this.x_scale.model)); - this.y_scale.set_range(this.parent.padded_range("y", this.y_scale.model)); + setRange(this.x_scale, this.parent.padded_range("x", this.x_scale.model)); + setRange(this.y_scale, this.parent.padded_range("y", this.y_scale.model)); // Called when the figure margins are updated. this.eventElement .attr("width", this.parent.width - diff --git a/js/lib/BrushRectangleSelector.js b/js/lib/BrushRectangleSelector.js index 4dadd5a..329562d 100644 --- a/js/lib/BrushRectangleSelector.js +++ b/js/lib/BrushRectangleSelector.js @@ -11,7 +11,7 @@ const BaseXYSelector = __importStar(require("bqplot")).BaseXYSelector; const d3 = require("d3"); const d3_drag_1 = require("d3-drag"); const d3Selection = require("d3-selection"); -const { applyStyles } = require("./utils"); +const { applyStyles, setRange } = require("./utils"); const d3GetEvent = function () { return require("d3-selection").event; }.bind(this); class BrushRectangleSelector extends BaseXYSelector { @@ -189,8 +189,8 @@ class BrushRectangleSelector extends BaseXYSelector { } relayout() { super.relayout(); - this.x_scale.set_range(this.parent.padded_range("x", this.x_scale.model)); - this.y_scale.set_range(this.parent.padded_range("y", this.y_scale.model)); + setRange(this.x_scale, this.parent.padded_range("x", this.x_scale.model)); + setRange(this.y_scale, this.parent.padded_range("y", this.y_scale.model)); // Called when the figure margins are updated. this.eventElement .attr("width", this.parent.width - diff --git a/js/lib/MouseInteraction.js b/js/lib/MouseInteraction.js index db4732c..4911d6a 100644 --- a/js/lib/MouseInteraction.js +++ b/js/lib/MouseInteraction.js @@ -9,6 +9,7 @@ const d3 = require("d3"); const d3_drag_1 = require("d3-drag"); const _ = require("lodash"); const d3_selection_1 = require("d3-selection"); +const { setRange } = require("./utils"); const d3GetEvent = function () { return require("d3-selection").event || window.event; }.bind(this); const clickEvents = ['click', 'dblclick', 'mouseenter', 'mouseleave', 'contextmenu']; @@ -208,8 +209,8 @@ class MouseInteraction extends Interaction_1.Interaction { } updateScaleRanges() { - this.x_scale.set_range(this.parent.padded_range("x", this.x_scale.model)); - this.y_scale.set_range(this.parent.padded_range("y", this.y_scale.model)); + setRange(this.x_scale, this.parent.padded_range("x", this.x_scale.model)); + setRange(this.y_scale, this.parent.padded_range("y", this.y_scale.model)); } remove() { super.remove(); diff --git a/js/lib/bqplot-gl-loader-classic.js b/js/lib/bqplot-gl-loader-classic.js new file mode 100644 index 0000000..6bd65c9 --- /dev/null +++ b/js/lib/bqplot-gl-loader-classic.js @@ -0,0 +1,17 @@ +function loadBqplotGL() { + // Classic widget frontends load nbextensions through RequireJS. Keeping + // this AMD-only prevents bqplot 0.12 from fetching bqplot-gl eagerly. + var amdRequire = typeof window !== "undefined" && (window.requirejs || window.require); + if(!amdRequire) { + return Promise.reject(new Error("bqplot-image-gl with bqplot 0.13 requires bqplot-gl. Install and enable bqplot-gl, or use bqplot 0.12.")); + } + return new Promise((resolve, reject) => { + amdRequire(["bqplot-gl"], resolve, () => { + reject(new Error("bqplot-image-gl with bqplot 0.13 requires bqplot-gl. Install and enable bqplot-gl, or use bqplot 0.12.")); + }); + }); +} + +export { + loadBqplotGL +}; diff --git a/js/lib/bqplot-gl-loader.js b/js/lib/bqplot-gl-loader.js new file mode 100644 index 0000000..02bb4a0 --- /dev/null +++ b/js/lib/bqplot-gl-loader.js @@ -0,0 +1,11 @@ +function loadBqplotGL() { + // JupyterLab 4 resolves separately installed widget packages through + // module federation, so the labextension bundle must use dynamic import. + return import("bqplot-gl").catch(() => { + throw new Error("bqplot-image-gl with bqplot 0.13 requires bqplot-gl. Install and enable bqplot-gl, or use bqplot 0.12."); + }); +} + +export { + loadBqplotGL +}; diff --git a/js/lib/compat.js b/js/lib/compat.js new file mode 100644 index 0000000..4aa0252 --- /dev/null +++ b/js/lib/compat.js @@ -0,0 +1,76 @@ +var {loadBqplotGL} = require("./bqplot-gl-loader"); + +function is_bqplot_013_figure(fig) { + return Boolean(fig.extras); +} + +function get_bqplot_012_plotarea(fig) { + return { + width: fig.plotarea_width, + height: fig.plotarea_height, + }; +} + +function get_bqplot_013_plotarea(fig) { + return { + width: fig.plotareaWidth, + height: fig.plotareaHeight, + }; +} + +function register_bqplot_013_webgl_mark(mark_view) { + var marks = mark_view.parent.extras && mark_view.parent.extras.webGLMarks; + if(marks && marks.indexOf(mark_view) === -1) { + marks.push(mark_view); + } +} + +function ensure_bqplot_013_webgl_figure(mark_view) { + var fig = mark_view.parent; + if(!is_bqplot_013_figure(fig)) { + return Promise.resolve(); + } + if(fig.extras.webGLRequestRender) { + register_bqplot_013_webgl_mark(mark_view); + return Promise.resolve(); + } + return loadBqplotGL().then((bqplot_gl) => { + if(!bqplot_gl.initializeBqplotFigure) { + throw new Error("bqplot-gl did not export initializeBqplotFigure."); + } + bqplot_gl.initializeBqplotFigure(fig); + register_bqplot_013_webgl_mark(mark_view); + }); +} + +function request_bqplot_012_webgl_render(fig) { + if(fig.update_gl) { + fig.update_gl(); + } +} + +function request_bqplot_013_webgl_render(fig) { + if(fig.extras.webGLRequestRender) { + fig.extras.webGLRequestRender(); + } + // bqplot 0.13 creates Figure.extras before bqplot-gl has installed + // the request-render hook; early draw calls during super.render() + // are replayed after initialization. +} + +function request_webgl_render(mark_view) { + var fig = mark_view.parent; + if(is_bqplot_013_figure(fig)) { + request_bqplot_013_webgl_render(fig); + } else { + request_bqplot_012_webgl_render(fig); + } +} + +export { + ensure_bqplot_013_webgl_figure, + get_bqplot_012_plotarea, + get_bqplot_013_plotarea, + is_bqplot_013_figure, + request_webgl_render, +}; diff --git a/js/lib/contour.js b/js/lib/contour.js index b0f2f0b..bdef619 100644 --- a/js/lib/contour.js +++ b/js/lib/contour.js @@ -6,6 +6,7 @@ import * as d3contour from "d3-contour"; import * as d3geo from "d3-geo"; import * as d3 from "d3"; import * as jupyter_dataserializers from "jupyter-dataserializers"; +import {colorRange, domain, setRange} from "./utils"; class ContourModel extends bqplot.MarkModel { @@ -141,12 +142,13 @@ class ContourView extends bqplot.Mark { return color_array[index % color_array.length]; } const model = this.model; - var colors = this.scales.image.model.color_range; + var colors = colorRange(this.scales.image.model); + var color_domain = domain(this.scales.image.model); var color_scale = d3.scaleLinear() .range(colors) - .domain(this.scales.image.model.domain); - const min = this.scales.image.model.domain[0]; - const max = this.scales.image.model.domain[this.scales.image.model.domain.length-1]; + .domain(color_domain); + const min = color_domain[0]; + const max = color_domain[color_domain.length-1]; const delta = max - min; // a good default color is one that is 50% off from the value of the colormap const level_plus_50_percent = ((threshold - min) + delta / 2) % delta + min; @@ -179,10 +181,10 @@ class ContourView extends bqplot.Mark { var x_scale = this.scales.x, y_scale = this.scales.y; if(x_scale) { - x_scale.set_range(this.parent.padded_range("x", x_scale.model)); + setRange(x_scale, this.parent.padded_range("x", x_scale.model)); } if(y_scale) { - y_scale.set_range(this.parent.padded_range("y", y_scale.model)); + setRange(y_scale, this.parent.padded_range("y", y_scale.model)); } } draw() { diff --git a/js/lib/imagegl.js b/js/lib/imagegl.js index 75fa0e4..cccb50c 100644 --- a/js/lib/imagegl.js +++ b/js/lib/imagegl.js @@ -7,6 +7,13 @@ var _ = require('lodash'); var d3 = require("d3"); var bqplot = require('bqplot'); var THREE = require('three'); +var {colorRange, computeAndSetDomain, delDomain, setRange} = require('./utils'); +var { + ensure_bqplot_013_webgl_figure, + get_bqplot_012_plotarea, + get_bqplot_013_plotarea, + request_webgl_render, +} = require("./compat"); var interpolations = {'nearest': THREE.NearestFilter, 'bilinear': THREE.LinearFilter}; @@ -51,7 +58,7 @@ class ImageGLModel extends bqplot.MarkModel { close(comm_closed) { const image = this.get("image"); if(image.image && image.image.src) { - URL.revokeObjectURL(previous.image.src); + URL.revokeObjectURL(image.image.src); } return super.close(comm_closed); } @@ -74,16 +81,16 @@ class ImageGLModel extends bqplot.MarkModel { if(x_scale) { if(!this.get("preserve_domain").x) { - x_scale.compute_and_set_domain(this.mark_data.x, this.model_id + "_x"); + computeAndSetDomain(x_scale, this.mark_data.x, this.model_id + "_x"); } else { - x_scale.del_domain([], this.model_id + "_x"); + delDomain(x_scale, [], this.model_id + "_x"); } } if(y_scale) { if(!this.get("preserve_domain").y) { - y_scale.compute_and_set_domain(this.mark_data.y, this.model_id + "_y"); + computeAndSetDomain(y_scale, this.mark_data.y, this.model_id + "_y"); } else { - y_scale.del_domain([], this.model_id + "_y"); + delDomain(y_scale, [], this.model_id + "_y"); } } } @@ -176,7 +183,7 @@ class ImageGLView extends bqplot.Mark { this.scene = new THREE.Scene(); this.scene.add(this.image_mesh); - return base_render_promise.then(() => { + return base_render_promise.then(() => ensure_bqplot_013_webgl_figure(this)).then(() => { this.create_listeners(); this.update_minmax(); this.update_colormap(); @@ -209,10 +216,10 @@ class ImageGLView extends bqplot.Mark { var x_scale = this.scales.x, y_scale = this.scales.y; if(x_scale) { - x_scale.set_range(this.parent.padded_range("x", x_scale.model)); + setRange(x_scale, this.parent.padded_range("x", x_scale.model)); } if(y_scale) { - y_scale.set_range(this.parent.padded_range("y", y_scale.model)); + setRange(y_scale, this.parent.padded_range("y", y_scale.model)); } } @@ -293,7 +300,7 @@ class ImageGLView extends bqplot.Mark { } // convert the d3 color scale to a texture - var colors = this.scales.image.model.color_range; + var colors = colorRange(this.scales.image.model); var color_scale = d3.scaleLinear() .range(colors) .domain(_.range(colors.length).map((i) => i/(colors.length-1))); @@ -370,23 +377,31 @@ class ImageGLView extends bqplot.Mark { } update_scene(animate) { - this.parent.update_gl(); + request_webgl_render(this); } - render_gl() { + render_bqplot_012_webgl() { var fig = this.parent; - var renderer = fig.renderer; - var image = this.model.get("image"); + var plotarea = get_bqplot_012_plotarea(fig); + this.render_webgl_mark(fig.renderer, this.camera, plotarea.width, plotarea.height); + } + + render_bqplot_013_webgl() { + var fig = this.parent; + var plotarea = get_bqplot_013_plotarea(fig); + this.render_webgl_mark(fig.extras.webGLRenderer.renderer, this.camera, plotarea.width, plotarea.height); + } + render_webgl_mark(renderer, camera, plotarea_width, plotarea_height) { var x_scale = this.scales.x ? this.scales.x : this.parent.scale_x; var y_scale = this.scales.y ? this.scales.y : this.parent.scale_y; // set the camera such that we work in pixel coordinates - this.camera.left = 0; - this.camera.right = fig.plotarea_width; - this.camera.bottom = 0; - this.camera.top = fig.plotarea_height; - this.camera.updateProjectionMatrix(); + camera.left = 0; + camera.right = plotarea_width; + camera.bottom = 0; + camera.top = plotarea_height; + camera.updateProjectionMatrix(); var x = this.model.get('x'); var y = this.model.get('y'); @@ -397,7 +412,7 @@ class ImageGLView extends bqplot.Mark { var pixel_width = x1_pixel - x0_pixel; var pixel_height = y1_pixel - y0_pixel; - this.image_mesh.position.set(x0_pixel + pixel_width/2, fig.plotarea_height - (y0_pixel + pixel_height/2), 0); + this.image_mesh.position.set(x0_pixel + pixel_width/2, plotarea_height - (y0_pixel + pixel_height/2), 0); this.image_mesh.scale.set(pixel_width, pixel_height, 1); this.image_material.uniforms.range_x.value = x_scale.scale.range(); @@ -409,10 +424,18 @@ class ImageGLView extends bqplot.Mark { this.image_material.uniforms.image_domain_x.value = [x0, x1]; this.image_material.uniforms.image_domain_y.value = [y0, y1]; - renderer.render(this.scene, this.camera); + renderer.render(this.scene, camera); var canvas = renderer.domElement; } + render_gl() { + this.render_bqplot_012_webgl(); + } + + renderGL() { + this.render_bqplot_013_webgl(); + } + relayout() { this.update_scene(); } diff --git a/js/lib/linesgl.js b/js/lib/linesgl.js index 07d2656..4aebe23 100644 --- a/js/lib/linesgl.js +++ b/js/lib/linesgl.js @@ -5,6 +5,12 @@ var d3 = require("d3"); var bqplot = require('bqplot'); var THREE = require('three'); var values = require('./values'); +var { + ensure_bqplot_013_webgl_figure, + get_bqplot_012_plotarea, + get_bqplot_013_plotarea, + request_webgl_render, +} = require("./compat"); var Line2 = require('./examples/lines/Line2').Line2; var LineMaterial = require('./examples/lines/LineMaterial').LineMaterial; @@ -61,6 +67,7 @@ class LinesGLView extends bqplot.Lines { const result = await super.render(); window.lastLinesGLView = this; + await ensure_bqplot_013_webgl_figure(this); this.material.onBeforeCompile = (shader) => { @@ -176,30 +183,48 @@ class LinesGLView extends bqplot.Lines { render_gl() { + this.render_bqplot_012_webgl(); + } + + renderGL() { + this.render_bqplot_013_webgl(); + } + + render_bqplot_012_webgl() { var fig = this.parent; - var renderer = fig.renderer; - this.camera.left = 0; - this.camera.right = fig.plotarea_width; - this.camera.bottom = 0; - this.camera.top = fig.plotarea_height; - this.camera.updateProjectionMatrix(); + var plotarea = get_bqplot_012_plotarea(fig); + this.render_webgl_mark(fig.renderer, this.camera, plotarea.width, plotarea.height); + } + + render_bqplot_013_webgl() { + var fig = this.parent; + var plotarea = get_bqplot_013_plotarea(fig); + this.render_webgl_mark(fig.extras.webGLRenderer.renderer, fig.extras.webGLRenderer.camera, plotarea.width, plotarea.height); + } + + render_webgl_mark(renderer, camera, plotarea_width, plotarea_height) { + camera.left = 0; + camera.right = plotarea_width; + camera.bottom = 0; + camera.top = plotarea_height; + camera.updateProjectionMatrix(); const x_scale = this.scales.x ? this.scales.x : this.parent.scale_x; const y_scale = this.scales.y ? this.scales.y : this.parent.scale_y; const range_x = this.parent.padded_range('x', x_scale.model); const range_y = this.parent.padded_range('y', y_scale.model); this.uniforms[`range_x`].value = range_x; - this.uniforms['resolution'].value = [fig.plotarea_width, fig.plotarea_height]; + this.uniforms['resolution'].value = [plotarea_width, plotarea_height]; this.uniforms[`range_y`].value = [range_y[1], range_y[0]]; // flipped coordinates in WebGL // every line cleans the depth buffer, since we have to draw with depthFunc: THREE.LessDepth // if we don't do this, we will not overdraw on other lines // A possible alternative would be to give each mark a z value according to index in Figure.marks renderer.clearDepth(); - renderer.render(this.scene, this.camera); + renderer.render(this.scene, camera); } update_scene(animate) { - this.parent.update_gl(); + request_webgl_render(this); } relayout() { diff --git a/js/lib/utils.js b/js/lib/utils.js index 290d814..aa2c6e8 100644 --- a/js/lib/utils.js +++ b/js/lib/utils.js @@ -18,3 +18,32 @@ export function is_typedarray(obj) { return isTypedArray(obj); } + +export +function setRange(scaleView, range) { + return scaleView.setRange ? scaleView.setRange(range) : scaleView.set_range(range); +} + +export +function computeAndSetDomain(scaleModel, values, id) { + return scaleModel.computeAndSetDomain ? + scaleModel.computeAndSetDomain(values, id) : + scaleModel.compute_and_set_domain(values, id); +} + +export +function delDomain(scaleModel, values, id) { + return scaleModel.delDomain ? + scaleModel.delDomain(values, id) : + scaleModel.del_domain(values, id); +} + +export +function colorRange(scaleModel) { + return scaleModel.colorRange || scaleModel.color_range; +} + +export +function domain(scaleModel) { + return scaleModel.domain || scaleModel.get("domain"); +} diff --git a/js/package-lock.json b/js/package-lock.json index 14840e6..4e84ce6 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "dependencies": { "@jupyter-widgets/base": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4 || ^6", - "bqplot": "^0.5.3", + "bqplot": "^0.5.3 || ^0.6.0", "d3": "^5.7.0", "d3-color": "^3.1.0", "d3-contour": "^4.0.2", @@ -24,11 +24,20 @@ }, "devDependencies": { "@jupyterlab/builder": "^3.0.0-rc.4 || ^4.0.0", + "bqplot-gl": "^0.1.0", "npm-run-all": "^4.1.5", "raw-loader": "^4", "rimraf": "^6.1.3", "webpack": "^5", "webpack-cli": "^7" + }, + "peerDependencies": { + "bqplot-gl": ">=0.1.0 <0.2.0" + }, + "peerDependenciesMeta": { + "bqplot-gl": { + "optional": true + } } }, "node_modules/@discoveryjs/json-ext": { @@ -427,7 +436,6 @@ "version": "2.4.8", "resolved": "https://registry.npmjs.org/@lumino/application/-/application-2.4.8.tgz", "integrity": "sha512-6Ohy3btpt8pRdv1wGxZpQZRxyxRDGYmAMTxhaHu1plu+dtLspkOQh8c8w1Nwtxw9L2862LYHBzt3ZcoTpHnURA==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@lumino/commands": "^2.3.3", @@ -1056,23 +1064,51 @@ } }, "node_modules/bqplot": { - "version": "0.5.46", - "resolved": "https://registry.npmjs.org/bqplot/-/bqplot-0.5.46.tgz", - "integrity": "sha512-qMyo11dhkEeVgIXXGfhXVQxNk89tplePMRt9WGvJOI/HzBO6Vr3c7JJj3KMQBbc8LF+E7o4OGcLY6Ez+Y2Y1hg==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/bqplot/-/bqplot-0.6.0.tgz", + "integrity": "sha512-IeidveIjUPWG/qNB7yyDpAYS53np0elw93ScfgK8yAygROjKfUogACA7nPquiqnLPMxyxd0UPTwk26tywwGMTA==", "license": "Apache-2.0", "dependencies": { "@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6", - "@lumino/messaging": "^1", - "@lumino/widgets": "^1", + "@lumino/messaging": "^1 || ^2", + "@lumino/widgets": "^1 || ^2", + "bqscales": "^0.3.2", "d3": "^5.7.0", "d3-selection": "^1", "is-typedarray": "^1.0.0", + "kiwi.js": "^1.1.2", "popper.js": "^1.0.0", - "three": "^0.91.0", "topojson": "^1.6.24", "underscore": "^1.8.3" } }, + "node_modules/bqplot-gl": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bqplot-gl/-/bqplot-gl-0.1.0.tgz", + "integrity": "sha512-vppqFCmjndc3IRDc/nijQftFr/FBSRt+u7zCpabwMIMGxoNC97R86T8AUfSdfYFqg/AVHoLeFU7ViaEAQl99Xg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyter-widgets/base": "^1.1.10 || ^2 || ^3 || ^4 || ^5 || ^6", + "@lumino/application": "^1 || ^2", + "@lumino/widgets": "^1 || ^2", + "@types/underscore": "^1.11.5", + "bqplot": "^0.6.0-alpha.0", + "bqscales": "^0.3.2", + "d3": "^5.7.0", + "is-typedarray": "^1.0.0", + "lodash": "^4.17.21", + "three": "^0.91.0", + "underscore": "^1.13.6" + } + }, + "node_modules/bqplot-gl/node_modules/three": { + "version": "0.91.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.91.0.tgz", + "integrity": "sha512-dzikzdcddNROFZi3vkbV8YonBmqnonbJv2FxlQBEE2wKzZutddnjiS8qBZG2+EB40l505Xw8OMClQm+GmbwI/g==", + "dev": true, + "license": "MIT" + }, "node_modules/bqplot/node_modules/@lumino/algorithm": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@lumino/algorithm/-/algorithm-1.9.2.tgz", @@ -1088,121 +1124,82 @@ "@lumino/algorithm": "^1.9.2" } }, - "node_modules/bqplot/node_modules/@lumino/commands": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/@lumino/commands/-/commands-1.21.1.tgz", - "integrity": "sha512-d1zJmwz5bHU0BM/Rl3tRdZ7/WgXnFB0bM7x7Bf0XDlmX++jnU9k0j3mh6/5JqCGLmIApKCRwVqSaV7jPmSJlcQ==", + "node_modules/bqplot/node_modules/@lumino/messaging": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@lumino/messaging/-/messaging-1.10.3.tgz", + "integrity": "sha512-F/KOwMCdqvdEG8CYAJcBSadzp6aI7a47Fr60zAKGqZATSRRRV41q53iXU7HjFPqQqQIvdn9Z7J32rBEAyQAzww==", "license": "BSD-3-Clause", "dependencies": { "@lumino/algorithm": "^1.9.2", - "@lumino/coreutils": "^1.12.1", - "@lumino/disposable": "^1.10.4", - "@lumino/domutils": "^1.8.2", - "@lumino/keyboard": "^1.8.2", - "@lumino/signaling": "^1.11.1", - "@lumino/virtualdom": "^1.14.3" - } - }, - "node_modules/bqplot/node_modules/@lumino/coreutils": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-1.12.1.tgz", - "integrity": "sha512-JLu3nTHzJk9N8ohZ85u75YxemMrmDzJdNgZztfP7F7T7mxND3YVNCkJG35a6aJ7edu1sIgCjBxOvV+hv27iYvQ==", - "license": "BSD-3-Clause", - "peerDependencies": { - "crypto": "1.0.1" + "@lumino/collections": "^1.9.3" } }, - "node_modules/bqplot/node_modules/@lumino/disposable": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@lumino/disposable/-/disposable-1.10.4.tgz", - "integrity": "sha512-4ZxyYcyzUS+ZeB2KAH9oAH3w0DUUceiVr+FIZHZ2TAYGWZI/85WlqJtfm0xjwEpCwLLW1TDqJrISuZu3iMmVMA==", - "license": "BSD-3-Clause", + "node_modules/bqscales": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/bqscales/-/bqscales-0.3.7.tgz", + "integrity": "sha512-nn2xq8SAY08C9vC15ceqE8Y6NSGJsNQ5tlKCEA8QYf8Cp2heR+Hs99hfpgmMeVCLRLjdc5coEjWcH9hWTg49FA==", + "license": "Apache", "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/signaling": "^1.11.1" - } - }, - "node_modules/bqplot/node_modules/@lumino/domutils": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@lumino/domutils/-/domutils-1.8.2.tgz", - "integrity": "sha512-QIpMfkPJrs4GrWBuJf2Sn1fpyVPmvqUUAeD8xAQo8+4V5JAT0vUDLxZ9HijefMgNCi3+Bs8Z3lQwRCrz+cFP1A==", - "license": "BSD-3-Clause" - }, - "node_modules/bqplot/node_modules/@lumino/dragdrop": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/@lumino/dragdrop/-/dragdrop-1.14.5.tgz", - "integrity": "sha512-LC5xB82+xGF8hFyl716TMpV32OIMIMl+s3RU1PaqDkD6B7PkgiVk6NkJ4X9/GcEvl2igkvlGQt/3L7qxDAJNxw==", + "@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6", + "@lumino/application": "^1.7.3 || ^2", + "@lumino/widgets": "^1.9.3 || ^2", + "d3-array": "^2.4.0", + "d3-geo": "^1.11.9", + "d3-scale": "^3.2.1", + "underscore": "^1.9.2" + } + }, + "node_modules/bqscales/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", "license": "BSD-3-Clause", "dependencies": { - "@lumino/coreutils": "^1.12.1", - "@lumino/disposable": "^1.10.4" + "internmap": "^1.0.0" } }, - "node_modules/bqplot/node_modules/@lumino/keyboard": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@lumino/keyboard/-/keyboard-1.8.2.tgz", - "integrity": "sha512-Dy+XqQ1wXbcnuYtjys5A0pAqf4SpAFl9NY6owyIhXAo0Va7w3LYp3jgiP1xAaBAwMuUppiUAfrbjrysZuZ625g==", - "license": "BSD-3-Clause" - }, - "node_modules/bqplot/node_modules/@lumino/messaging": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/@lumino/messaging/-/messaging-1.10.3.tgz", - "integrity": "sha512-F/KOwMCdqvdEG8CYAJcBSadzp6aI7a47Fr60zAKGqZATSRRRV41q53iXU7HjFPqQqQIvdn9Z7J32rBEAyQAzww==", + "node_modules/bqscales/node_modules/d3-geo": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", + "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/collections": "^1.9.3" + "d3-array": "1" } }, - "node_modules/bqplot/node_modules/@lumino/properties": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@lumino/properties/-/properties-1.8.2.tgz", - "integrity": "sha512-EkjI9Cw8R0U+xC9HxdFSu7X1tz1H1vKu20cGvJ2gU+CXlMB1DvoYJCYxCThByHZ+kURTAap4SE5x8HvKwNPbig==", + "node_modules/bqscales/node_modules/d3-geo/node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==", "license": "BSD-3-Clause" }, - "node_modules/bqplot/node_modules/@lumino/signaling": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@lumino/signaling/-/signaling-1.11.1.tgz", - "integrity": "sha512-YCUmgw08VoyMN5KxzqPO3KMx+cwdPv28tAN06C0K7Q/dQf+oufb1XocuhZb5selTrTmmuXeizaYxgLIQGdS1fA==", + "node_modules/bqscales/node_modules/d3-scale": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", + "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/properties": "^1.8.2" + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "^2.1.1", + "d3-time-format": "2 - 3" } }, - "node_modules/bqplot/node_modules/@lumino/virtualdom": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@lumino/virtualdom/-/virtualdom-1.14.3.tgz", - "integrity": "sha512-5joUC1yuxeXbpfbSBm/OR8Mu9HoTo6PDX0RKqzlJ9o97iml7zayFN/ynzcxScKGQAo9iaXOY8uVIvGUT8FnsGw==", + "node_modules/bqscales/node_modules/d3-time": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^1.9.2" + "d3-array": "2" } }, - "node_modules/bqplot/node_modules/@lumino/widgets": { - "version": "1.37.2", - "resolved": "https://registry.npmjs.org/@lumino/widgets/-/widgets-1.37.2.tgz", - "integrity": "sha512-NHKu1NBDo6ETBDoNrqSkornfUCwc8EFFzw6+LWBfYVxn2PIwciq2SdiJGEyNqL+0h/A9eVKb5ui5z4cwpRekmQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/commands": "^1.21.1", - "@lumino/coreutils": "^1.12.1", - "@lumino/disposable": "^1.10.4", - "@lumino/domutils": "^1.8.2", - "@lumino/dragdrop": "^1.14.5", - "@lumino/keyboard": "^1.8.2", - "@lumino/messaging": "^1.10.3", - "@lumino/properties": "^1.8.2", - "@lumino/signaling": "^1.11.1", - "@lumino/virtualdom": "^1.14.3" - } - }, - "node_modules/bqplot/node_modules/three": { - "version": "0.91.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.91.0.tgz", - "integrity": "sha512-dzikzdcddNROFZi3vkbV8YonBmqnonbJv2FxlQBEE2wKzZutddnjiS8qBZG2+EB40l505Xw8OMClQm+GmbwI/g==", - "license": "MIT" + "node_modules/bqscales/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" }, "node_modules/brace-expansion": { "version": "1.1.13", @@ -1553,14 +1550,6 @@ "semver": "bin/semver" } }, - "node_modules/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", - "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", - "license": "ISC", - "peer": true - }, "node_modules/css-loader": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", @@ -3632,6 +3621,12 @@ "node": ">=0.10.0" } }, + "node_modules/kiwi.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/kiwi.js/-/kiwi.js-1.1.3.tgz", + "integrity": "sha512-aBA32N0LjuF0RIeV+OVhT8wnGAwdLYynKxkHbRvatI7yajOBM3OUjzTuzt3w9UnY9TYt7U65oe7U8EXlUHuTrQ==", + "license": "BSD-3-Clause" + }, "node_modules/klaw-sync": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", diff --git a/js/package.json b/js/package.json index 90b8cf6..f08f99e 100644 --- a/js/package.json +++ b/js/package.json @@ -33,6 +33,7 @@ }, "devDependencies": { "@jupyterlab/builder": "^3.0.0-rc.4 || ^4.0.0", + "bqplot-gl": "^0.1.0", "npm-run-all": "^4.1.5", "raw-loader": "^4", "rimraf": "^6.1.3", @@ -41,7 +42,7 @@ }, "dependencies": { "@jupyter-widgets/base": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4 || ^6", - "bqplot": "^0.5.3", + "bqplot": "^0.5.3 || ^0.6.0", "d3": "^5.7.0", "d3-color": "^3.1.0", "d3-contour": "^4.0.2", @@ -53,6 +54,14 @@ "patch-package": "^8.0.1", "three": "^0.97.0" }, + "peerDependencies": { + "bqplot-gl": ">=0.1.0 <0.2.0" + }, + "peerDependenciesMeta": { + "bqplot-gl": { + "optional": true + } + }, "jupyterlab": { "extension": "lib/labplugin", "outputDir": "../bqplot_image_gl/labextension", @@ -65,6 +74,10 @@ "bundled": false, "singleton": true }, + "bqplot-gl": { + "bundled": false, + "singleton": true + }, "d3": { "bundled": false }, diff --git a/js/webpack.config.js b/js/webpack.config.js index de9d106..f57e413 100644 --- a/js/webpack.config.js +++ b/js/webpack.config.js @@ -8,6 +8,18 @@ var rules = [ { test: /\.css$/, use: ['style-loader', 'css-loader']} ] +var widgetExternals = { + '@jupyter-widgets/base': '@jupyter-widgets/base', + 'bqplot': 'bqplot', + 'bqplot-gl': 'bqplot-gl', +}; + +var classicResolve = { + alias: { + './bqplot-gl-loader': path.resolve(__dirname, 'lib/bqplot-gl-loader-classic.js'), + }, +}; + module.exports = [ {// Notebook extension @@ -45,7 +57,8 @@ module.exports = [ module: { rules: rules }, - externals: ['@jupyter-widgets/base', 'bqplot'] + resolve: classicResolve, + externals: widgetExternals }, {// Embeddable bqplot-image-gl bundle // @@ -73,6 +86,7 @@ module.exports = [ module: { rules: rules }, - externals: ['@jupyter-widgets/base', 'bqplot'] + resolve: classicResolve, + externals: widgetExternals } ]; diff --git a/pyproject.toml b/pyproject.toml index 6c79eaf..658160a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,11 +31,14 @@ classifiers = [ ] dependencies = [ "ipywidgets>=7.0.0", - "bqplot>=0.12", + "bqplot>=0.12,<0.14", "pillow", ] [project.optional-dependencies] +bqplot13 = [ + "bqplot-gl>=0.1.1", +] test = [ "pytest", "pytest-cov", @@ -50,6 +53,12 @@ path = "bqplot_image_gl/_version.py" [tool.hatch.build.targets.sdist] exclude = [ ".github", + ".tox", + ".venv*", + "env", + "js/_nm*", + "results", + "widget-dev", ] [tool.hatch.build.targets.wheel] diff --git a/tox.ini b/tox.ini index b58cfd4..3fbaac1 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,8 @@ requires = pip >= 18.0 isolated_build = true [testenv] +setenv = + bqplot013: SOLARA_TEST_RUNNERS = solara,jupyter_notebook changedir = test: .tmp/{envname} notebooks: examples @@ -16,8 +18,16 @@ deps = visual: playwright==1.41.2 visual: pytest-playwright==0.5.2 visual: pytest-mpl>=0.19.0 - visual: solara[pytest]>=1.44.1 visual: starlette<1.0 + bqplot012: solara[pytest]>=1.44.1 + bqplot012: bqplot>=0.12,<0.13 + bqplot012: jupyterlab<4 + bqplot013: pytest-ipywidgets==1.57.3 + bqplot013: solara>=1.44.1 + bqplot013: notebook<7 + bqplot013: bqplot>=0.13,<0.14 + bqplot013: bqplot-gl @ git+https://github.com/maartenbreddels/bqplot-gl.git@codex/export-webgl-figure-init + bqplot013: jupyterlab>=4,<5 extras = test: test notebooks: test