From eb6d70fa06444de2f7be32e772b2e79dbffa3ed7 Mon Sep 17 00:00:00 2001 From: Tom Kazimiers Date: Fri, 18 Oct 2024 11:38:06 +0200 Subject: [PATCH] Landmark widget: remove cached landmark layer data on transform removal Fixes catmaid/catmaid#2268 --- .../static/js/layers/landmark-layer.js | 368 +++++++++--------- .../static/js/widgets/landmark-widget.js | 1 + 2 files changed, 192 insertions(+), 177 deletions(-) diff --git a/django/applications/catmaid/static/js/layers/landmark-layer.js b/django/applications/catmaid/static/js/layers/landmark-layer.js index 45ddb1dcf1..38019de5e7 100644 --- a/django/applications/catmaid/static/js/layers/landmark-layer.js +++ b/django/applications/catmaid/static/js/layers/landmark-layer.js @@ -143,6 +143,17 @@ this.displayTransformations.push(transformation); }; + /** + * Remove a transformation from this layer. + */ + LandmarkLayer.prototype.removeLandmarkTransform = function(transformation) { + let txIndex = this.displayTransformations.indexOf(transformation); + if (txIndex < -1) { + return; + } + this.displayTransformations.splice(txIndex, 1); + }; + /** * Adjust rendering to current field of view. */ @@ -152,113 +163,111 @@ var projectViewBox = this.stackViewer.primaryStack.createStackToProjectBox(stackViewBox); // If there are nodes available, find the ones on the current section - if (this._currentZIndex.size > 0) { - let zIndex = this._currentZIndex; - let currentZ = this.stackViewer.z; - let primaryStack = this.stackViewer.primaryStack; - // Render intersection of each available skeleton with the current - // section. - let nodesOnSection = zIndex.get(this.stackViewer.z); - - if (nodesOnSection && nodesOnSection.size > 0) { - this.nodes = new Map(); - // Prepare existing Node and ConnectorNode instances for reuse - this.graphics.resetCache(); - var addedNodes = []; - - // Add regular nodes - for (let a of nodesOnSection) { - // Add all nodes along with their parents and children - // [id, parent_id, user_id, location_x, location_y, location_z, radius, confidence]. - var stackZ = primaryStack.projectToUnclampedStackZ(a[5], a[4], a[3]); - let newNode = this.graphics.newNode(a[0], null, a[1], a[6], - a[3], a[4], a[5], stackZ - currentZ, a[7], a[8], - 0, a[2]); - this.nodes.set(a[0], newNode); - addedNodes.push(newNode); - } + let zIndex = this._currentZIndex; + let currentZ = this.stackViewer.z; + let primaryStack = this.stackViewer.primaryStack; + // Render intersection of each available skeleton with the current + // section. + let nodesOnSection = zIndex.get(this.stackViewer.z); + + if (nodesOnSection && nodesOnSection.size > 0) { + this.nodes = new Map(); + // Prepare existing Node and ConnectorNode instances for reuse + this.graphics.resetCache(); + var addedNodes = []; + + // Add regular nodes + for (let a of nodesOnSection) { + // Add all nodes along with their parents and children + // [id, parent_id, user_id, location_x, location_y, location_z, radius, confidence]. + var stackZ = primaryStack.projectToUnclampedStackZ(a[5], a[4], a[3]); + let newNode = this.graphics.newNode(a[0], null, a[1], a[6], + a[3], a[4], a[5], stackZ - currentZ, a[7], a[8], + 0, a[2]); + this.nodes.set(a[0], newNode); + addedNodes.push(newNode); + } - // Add virtual nodes and link parent with children - for (let b of nodesOnSection) { - var n = this.nodes.get(b[0]); - var pn = this.nodes.get(b[1]); // parent Node + // Add virtual nodes and link parent with children + for (let b of nodesOnSection) { + var n = this.nodes.get(b[0]); + var pn = this.nodes.get(b[1]); // parent Node - // Neither virtual nodes or other parent/child links need to be created if - // there is no parent node. - if (!pn) { - continue; - } + // Neither virtual nodes or other parent/child links need to be created if + // there is no parent node. + if (!pn) { + continue; + } - // Virtual nodes can only exists if both parent and child are not on the - // current section and not both above or below. - if ((n.zdiff < 0 && pn.zdiff > 0) || (n.zdiff > 0 && pn.zdiff < 0)) { - var vn = CATMAID.createVirtualNode(this.graphics, n, pn, this.stackViewer); - if (vn) { - n.parent = vn; - n.parent_id = vn.id; - pn.addChildNode(vn); - vn.addChildNode(n); - this.nodes.set(vn.id, vn); - addedNodes.push(vn); - continue; - } + // Virtual nodes can only exists if both parent and child are not on the + // current section and not both above or below. + if ((n.zdiff < 0 && pn.zdiff > 0) || (n.zdiff > 0 && pn.zdiff < 0)) { + var vn = CATMAID.createVirtualNode(this.graphics, n, pn, this.stackViewer); + if (vn) { + n.parent = vn; + n.parent_id = vn.id; + pn.addChildNode(vn); + vn.addChildNode(n); + this.nodes.set(vn.id, vn); + addedNodes.push(vn); + continue; } - - // If no virtual node was inserted, link parent and child normally. - n.parent = pn; - // update the parent's children - pn.addChildNode(n); } - // Disable most unused node instances, keeping a small caching buffer. - this.graphics.disableBeyond(addedNodes.length, 0); + // If no virtual node was inserted, link parent and child normally. + n.parent = pn; + // update the parent's children + pn.addChildNode(n); + } - this.initColors(); + // Disable most unused node instances, keeping a small caching buffer. + this.graphics.disableBeyond(addedNodes.length, 0); - // Draw node edges and circles, including the ones for virtual nodes. - for (var i=0, imax=addedNodes.length; i 0 && (userScaleFactor * this.stackViewer.s) !== this.lastScale) { - // Remember current zoom level - this.lastScale = userScaleFactor * this.stackViewer.s; - // Update edge width - var edgeWidth = this.graphics.Node.prototype.EDGE_WIDTH || 2; - this.graphics.containers.lines.children.forEach(function (line) { - line.graphicsData[0].lineWidth = edgeWidth; - line.dirty++; - line.clearDirty++; - }); - this.graphics.containers.nodes.children.forEach(function (c) { - c.scale.set(this.graphics.Node.prototype.stackScaling); - }, this); + // Update colors + for (let node of this.nodes.values()) { + node.updateColors(); } + } else { + for (let node of this.nodes.values()) { + node.disable(); + } + this.nodes = new Map(); + } + + var screenScale = CATMAID.TracingOverlay.Settings.session.screen_scaling; + // All graphics elements scale automatcally. + // If in screen scale mode, where the size of all elements should + // stay the same (regardless of zoom level), counter acting this is required. + var dynamicScale = screenScale ? (1 / this.stackViewer.scale) : false; + + let userScaleFactor = this.options.scale; + this.graphics.scale( + CATMAID.TracingOverlay.Settings.session.scale * userScaleFactor, + this.stackViewer.primaryStack.minPlanarRes, + dynamicScale); + + // In case of a zoom level change and screen scaling is selected, update + // edge width. + if (this.displayTransformations.length > 0 && (userScaleFactor * this.stackViewer.s) !== this.lastScale) { + // Remember current zoom level + this.lastScale = userScaleFactor * this.stackViewer.s; + // Update edge width + var edgeWidth = this.graphics.Node.prototype.EDGE_WIDTH || 2; + this.graphics.containers.lines.children.forEach(function (line) { + line.graphicsData[0].lineWidth = edgeWidth; + line.dirty++; + line.clearDirty++; + }); + this.graphics.containers.nodes.children.forEach(function (c) { + c.scale.set(this.graphics.Node.prototype.stackScaling); + }, this); } var planeDims = this.stackViewer.primaryStack.getPlaneDimensions(); @@ -354,100 +363,105 @@ let self = this; let availableSkeletonData = new Map(); - Promise.all(dataRetrievalJobs) - .then(function(results) { - self._loading = false; - - // Build stack Z based index - let zIndex = self._currentZIndex; - let stack = self.stackViewer.primaryStack; - zIndex.clear(); - - for (let i=0; i m.id); - - for (let j=0; j m.id); + + for (let j=0; j 0 ? -1 : 1; - for (let z=nodeStackZ; z !== parentStackZ; z += zStep) { - let isectZBucket = zIndex.get(z); - if (!isectZBucket) { - isectZBucket = new Set(); - zIndex.set(z, isectZBucket); + // Add parent node, if this is no root + let parentId = node[1]; + if (parentId) { + let parentNode = nodeMap.get(parentId); + zBucket.add(parentNode); + // Add parent and this node also to every section between them. + let parentStackZ = stackZMap.get(parentId); + let zDiff = nodeStackZ - parentStackZ; + let zStep = zDiff > 0 ? -1 : 1; + for (let z=nodeStackZ; z !== parentStackZ; z += zStep) { + let isectZBucket = zIndex.get(z); + if (!isectZBucket) { + isectZBucket = new Set(); + zIndex.set(z, isectZBucket); + } + isectZBucket.add(node); + isectZBucket.add(parentNode); } - isectZBucket.add(node); - isectZBucket.add(parentNode); } } } } - } - self.redraw(); - }) - .catch(CATMAID.handleError); + self.redraw(); + }) + .catch(CATMAID.handleError); + } }; LandmarkLayer.prototype.unregister = function() { diff --git a/django/applications/catmaid/static/js/widgets/landmark-widget.js b/django/applications/catmaid/static/js/widgets/landmark-widget.js index 4f631e51fb..d33971d4fc 100644 --- a/django/applications/catmaid/static/js/widgets/landmark-widget.js +++ b/django/applications/catmaid/static/js/widgets/landmark-widget.js @@ -807,6 +807,7 @@ for (let t of layerTransformations) { if (!transformations.has(t)) { layerTransformations.delete(t); + layer.removeLandmarkTransform(t); ++nRemoved; } }