From a726997a46b19150d3298b9125aa4b63cf48746f Mon Sep 17 00:00:00 2001 From: William Moore Date: Thu, 29 Feb 2024 16:13:23 +0000 Subject: [PATCH] Move Z/T slider logic into separate view --- src/css/figure.css | 9 +- src/index.html | 36 +--- src/js/models/panel_model.js | 2 +- src/js/views/channel_slider_view.js | 1 - src/js/views/right_panel_view.js | 233 +--------------------- src/js/views/zt_sliders_view.js | 287 +++++++++++++++++++++++++++ src/templates/viewport.template.html | 6 +- 7 files changed, 306 insertions(+), 268 deletions(-) create mode 100644 src/js/views/zt_sliders_view.js diff --git a/src/css/figure.css b/src/css/figure.css index b45b67dbf..d436ecc98 100644 --- a/src/css/figure.css +++ b/src/css/figure.css @@ -545,9 +545,14 @@ transform: rotate(90deg); } - .imageViewer { + .imageViewer, .ztSliders { height: 270px; } + .ztSliders { + position: absolute; + top: 0; + } + .vp_img { position:absolute; } @@ -582,7 +587,7 @@ } #vp_deltaT { position: absolute; - right: 100px; + left: 100px; bottom: -19px; } .no-padding { diff --git a/src/index.html b/src/index.html index 1fc8e3aaf..3e1c27dbd 100644 --- a/src/index.html +++ b/src/index.html @@ -1814,41 +1814,7 @@
Example Label
- - +
diff --git a/src/js/models/panel_model.js b/src/js/models/panel_model.js index 2167d4ff0..276adb997 100644 --- a/src/js/models/panel_model.js +++ b/src/js/models/panel_model.js @@ -1042,7 +1042,7 @@ let class_ = ann.link.parent.class; let id_ = '' + ann.link.parent.id; let lineage = data.parents.lineage[class_][id_]; - for(j = 0; j < lineage.length; j++){ + for(let j = 0; j < lineage.length; j++){ // Unpacking the parent annoations for each image let clone_ann = JSON.parse(JSON.stringify(ann)); clone_ann.link.parent.id = lineage[j].id; diff --git a/src/js/views/channel_slider_view.js b/src/js/views/channel_slider_view.js index 1aba8dffb..7f031bd26 100644 --- a/src/js/views/channel_slider_view.js +++ b/src/js/views/channel_slider_view.js @@ -307,7 +307,6 @@ var ChannelSliderView = Backbone.View.extend({ var active = actives.reduce(allEqualFn, actives[0]); var style = {'background-position': '0 0'} var lutBgPos = FigureLutPicker.getLutBackgroundPosition(color); - console.log("color", color); if (color.endsWith('.lut')) { style['background-position'] = lutBgPos; color = "ccc"; diff --git a/src/js/views/right_panel_view.js b/src/js/views/right_panel_view.js index ac5f2d335..11e471abc 100644 --- a/src/js/views/right_panel_view.js +++ b/src/js/views/right_panel_view.js @@ -14,6 +14,7 @@ import InfoPanelView from "./info_panel_view"; import ChannelSliderView from "./channel_slider_view"; import ScalebarFormView from "./scalebar_form_view"; + import ZtSlidersView from "./zt_sliders_view"; import image_display_options_template from '../../templates/image_display_options.template.html?raw'; import rois_form_template from '../../templates/rois_form.template.html?raw'; @@ -34,7 +35,6 @@ // this.render(); new LabelsPanelView({model: this.model}); - new SliderButtonsView({model: this.model}); new RoisFormView({model: this.model}); }, @@ -45,9 +45,15 @@ this.vp.clear().remove(); delete this.vp; // so we don't call clear() on it again. } + if (this.ztsliders) { + this.ztsliders.remove(); + delete this.ztsliders; + } if (selected.length > 0) { this.vp = new ImageViewerView({models: selected, figureModel: this.model}); // auto-renders on init $("#viewportContainer").append(this.vp.el); + this.ztsliders = new ZtSlidersView({models: selected}); + $("#viewportContainer").append(this.ztsliders.el); } if (this.ipv) { @@ -581,64 +587,6 @@ }); - // This simply handles buttons to increment time/z - // since other views don't have an appropriate container - var SliderButtonsView = Backbone.View.extend({ - - el: $("#viewportContainer"), - - initialize: function(opts) { - this.model = opts.model; - }, - - events: { - "click .z-increment": "z_increment", - "click .z-decrement": "z_decrement", - "click .time-increment": "time_increment", - "click .time-decrement": "time_decrement", - }, - - z_increment: function(event) { - this.model.getSelected().forEach(function(m){ - var newZ = {}; - if (m.get('z_projection')) { - newZ.z_start = m.get('z_start') + 1; - newZ.z_end = m.get('z_end') + 1; - } else { - newZ.theZ = m.get('theZ') + 1; - } - m.set(newZ, {'validate': true}); - }); - return false; - }, - z_decrement: function(event) { - this.model.getSelected().forEach(function(m){ - var newZ = {}; - if (m.get('z_projection')) { - newZ.z_start = m.get('z_start') - 1; - newZ.z_end = m.get('z_end') - 1; - } else { - newZ.theZ = m.get('theZ') - 1; - } - m.set(newZ, {'validate': true}); - }); - return false; - }, - time_increment: function(event) { - this.model.getSelected().forEach(function(m){ - m.set({'theT': m.get('theT') + 1}, {'validate': true}); - }); - return false; - }, - time_decrement: function(event) { - this.model.getSelected().forEach(function(m){ - m.set({'theT': m.get('theT') - 1}, {'validate': true}); - }); - return false; - }, - }); - - var ImageViewerView = Backbone.View.extend({ template: _.template(viewport_template), @@ -808,8 +756,6 @@ clear: function() { // clean up zoom slider etc $("#vp_zoom_slider").off(); - $("#vp_z_slider").hide(); - $("#vp_t_slider").hide(); $("#vp_zoom_value").off(); this.$vp_zoom_value.val(); @@ -869,24 +815,6 @@ this.zmView.renderXYWH(zoom, scaled_dx, scaled_dy); }, - formatTime: function(seconds) { - - if (typeof seconds === 'undefined') { - return ""; - } - var isNegative = seconds < 0; - seconds = Math.abs(seconds); - function leftPad(s) { - s = s + ''; - if (s.split('.')[0].length === 1) return '0' + s; - return s; - } - var hours = parseInt(seconds / 3600); - var mins = parseInt(seconds % 3600 / 60); - var secs = (seconds % 60).toFixed(2); - return (isNegative ? '-' : '') + leftPad(hours) + ":" + leftPad(mins) + ":" + leftPad(secs); - }, - get_imgs_css: function() { // Get img src & positioning css for each panel, var imgs_css = []; @@ -927,48 +855,6 @@ this.$vp_frame.append(html); // update this to include new images this.$vp_img = $(".vp_img", this.$el); - - this.render_z_t_labels(); - }, - - render_z_t_labels: function() { - // Rendering Z/T labels without rendering whole component - // means we don't re-build z/t sliders (they don't lose focus) - var sizeZ = this.models.getIfEqual('sizeZ'); - var sizeT = this.models.getIfEqual('sizeT'); - var theT = this.models.getAverage('theT'); - var theZ = this.models.getAverage('theZ'); - var z_projection = this.models.allTrue('z_projection'); - var deltaT = this.models.getDeltaTIfEqual(); - - var z_label = theZ + 1; - $("#vp_z_slider input[type='range']").val(theZ + 1) - if (z_projection) { - var z_start = Math.round(this.models.getAverage('z_start')); - var z_end = Math.round(this.models.getAverage('z_end')); - z_label = (z_start + 1) + "-" + (z_end + 1); - $("#vp_z_slider .z_end").val(z_end + 1); - $("#vp_z_slider .z_start").val(z_start + 1); - } else if (!this.models.allEqual('theZ')) { - z_label = "-"; - } - // $("#vp_z_slider").slider({'value': theZ + 1}); - $("#vp_z_value").text(z_label + "/" + (sizeZ || '-')); - - var t_label = theT + 1; - var dt_label; - if (!this.models.allEqual('theT')) { - t_label = "-"; - } - $("#vp_t_slider input[type='range']").val(theT + 1) - $("#vp_t_value").text(t_label + "/" + (sizeT || '-')); - - if ((deltaT === 0 || deltaT) && sizeT > 1) { - dt_label = this.formatTime(deltaT); - } else { - dt_label = ""; - } - $("#vp_deltaT").text(dt_label); }, render: function() { @@ -977,20 +863,9 @@ this.zmView.render(); // only show viewport if original w / h ratio is same for all models - var model = this.models.head(), - self = this; - var imgs_css; - // get average viewport frame w/h & zoom var wh = this.models.getAverageWH(), - zoom = this.models.getAverage('zoom'), - theZ = this.models.getAverage('theZ'), - z_start = Math.round(this.models.getAverage('z_start')), - z_end = Math.round(this.models.getAverage('z_end')), - theT = this.models.getAverage('theT'), - sizeZ = this.models.getIfEqual('sizeZ'), - sizeT = this.models.getIfEqual('sizeT'), - z_projection = this.models.allTrue('z_projection'); + zoom = this.models.getAverage('zoom'); if (wh <= 1) { var frame_h = this.full_size; @@ -1000,94 +875,7 @@ var frame_h = this.full_size / wh; } - imgs_css = this.get_imgs_css(); - - // update sliders - var Z_disabled = false, - Z_max = sizeZ; - if (!sizeZ || sizeZ === 1) { // undefined or 1 - Z_disabled = true; - Z_max = 1; - $("#vp_z_slider").hide(); - } else { - $("#vp_z_slider").show(); - } - - // setup single Z slider(s) - if (z_projection) { - // show the z_end slider... - $("#vp_z_slider .z_end").show().attr({max: Z_max, min: 1}).val(z_end + 1); - $("#vp_z_slider .z_start").show().attr({max: Z_max, min: 1}).val(z_start + 1); - // slide and stop events for both sliders - if (!Z_disabled) { - $("#vp_z_slider input[type='range']") - .on("input", (event) => { - let start = $("#vp_z_slider .z_start").val(); - let end = $("#vp_z_slider .z_end").val(); - $("#vp_z_value").text(start + "-" + end + "/" + sizeZ); - }) - .on("change", (event) => { - let start = $("#vp_z_slider .z_start").val(); - let end = $("#vp_z_slider .z_end").val(); - self.models.forEach(function(m){ - m.save({ - 'z_start': start - 1, - 'z_end': end -1 - }); - }); - }); - } - } else { - // hide the z_end slider (not needed) - $("#vp_z_slider .z_end").hide(); - $("#vp_z_slider .z_start") - .show() - .attr({max: sizeZ, min: 1}) - .val(theZ + 1) - .on("input", (event) => { - $("#vp_z_value").text(event.target.value + "/" + sizeZ); - }) - .on("change", (event) => { - self.models.forEach(function(m){ - m.save('theZ', event.target.value - 1); - }); - }); - } - - // T-slider should be enabled even if we have a mixture of sizeT values. - // Slider T_max is the minimum of sizeT values - // Slider value is average of theT values (but smaller than T_max) - var T_disabled = false, - T_slider_max = self.models.getMin('sizeT'); - if (T_slider_max === 1) { - T_disabled = true; - $("#vp_t_slider").hide(); - } else { - $("#vp_t_slider").show(); - } - var t_slider_value = Math.min(theT, T_slider_max); - - // setup T slider - $("#vp_t_slider input[type='range']") - .show() - .attr({max: T_slider_max, min: 1}) - .val(t_slider_value + 1) - .on("input", (event) => { - var theT = event.target.value; - $("#vp_t_value").text(theT + "/" + (sizeT || '-')); - var dt = self.models.head().get('deltaT')[theT-1]; - self.models.forEach(function(m){ - if (m.get('deltaT')[theT-1] != dt) { - dt = undefined; - } - }); - $("#vp_deltaT").text(self.formatTime(dt)); - }) - .on("change", (event) => { - self.models.forEach(function(m){ - m.save('theT', event.target.value - 1); - }); - }); + var imgs_css = this.get_imgs_css(); var json = {}; json.inner_template = this.inner_template; @@ -1096,11 +884,8 @@ json.frame_w = frame_w; json.frame_h = frame_h; - // We render without z/t labels... var html = this.template(json); this.$el.html(html); - // ...then update them dynamically - this.render_z_t_labels(); this.$vp_frame = $(".vp_frame", this.$el); // cache for later this.$vp_img = $(".vp_img", this.$el); diff --git a/src/js/views/zt_sliders_view.js b/src/js/views/zt_sliders_view.js new file mode 100644 index 000000000..b8fec637a --- /dev/null +++ b/src/js/views/zt_sliders_view.js @@ -0,0 +1,287 @@ + +import Backbone from "backbone"; +import _ from "underscore"; +import $ from "jquery"; + +// This simply handles buttons to increment time/z +// since other views don't have an appropriate container +const ZtSlidersView = Backbone.View.extend({ + + className: "ztSliders", + + initialize: function(opts) { + this.models = opts.models; + + this.models.forEach(m => { + this.listenTo(m, 'change:z_projection change:theT change:theZ change:z_start', this.render); + }); + + this.render(); + }, + + events: { + "click .z-increment": "z_increment", + "click .z-decrement": "z_decrement", + "click .time-increment": "time_increment", + "click .time-decrement": "time_decrement", + "input #vp_t_slider input[type='range']": "slide_time", + "change #vp_t_slider input[type='range']": "slidestop_time", + "input #vp_z_slider input[type='range']": "slide_z", + "change #vp_z_slider input[type='range']": "slidestop_z", + }, + + slide_time(event) { + var theT = event.target.value; + $("#vp_t_value").text(theT + "/" + (this.sizeT || '-')); + var dt = this.models.head().get('deltaT')[theT-1]; + this.models.forEach(function(m){ + if (m.get('deltaT')[theT-1] != dt) { + dt = undefined; + } + }); + $("#vp_deltaT").text(this.formatTime(dt)); + }, + + slidestop_time(event) { + this.models.forEach(function(m){ + m.save('theT', event.target.value - 1); + }); + }, + + slide_z(event) { + // Handles z_start and z_end sliders + // TODO: cache z_projection? + var z_projection = this.models.allTrue('z_projection'); + let start = $("#vp_z_slider .z_start").val(); + let end = $("#vp_z_slider .z_end").val(); + if (z_projection) { + start = start + "-" + end; + } + $("#vp_z_value").text(start + "/" + this.sizeZ); + }, + + slidestop_z(event) { + var z_projection = this.models.allTrue('z_projection'); + let start = $("#vp_z_slider .z_start").val(); + let end = $("#vp_z_slider .z_end").val(); + + console.log("slidestop Z", z_projection, start, end); + this.models.forEach(function(m){ + if (z_projection) { + m.save({ + 'z_start': start - 1, + 'z_end': end - 1 + }); + } else { + m.save('theZ', event.target.value - 1); + } + }); + }, + + formatTime: function(seconds) { + + if (typeof seconds === 'undefined') { + return ""; + } + var isNegative = seconds < 0; + seconds = Math.abs(seconds); + function leftPad(s) { + s = s + ''; + if (s.split('.')[0].length === 1) return '0' + s; + return s; + } + var hours = parseInt(seconds / 3600); + var mins = parseInt(seconds % 3600 / 60); + var secs = (seconds % 60).toFixed(2); + return (isNegative ? '-' : '') + leftPad(hours) + ":" + leftPad(mins) + ":" + leftPad(secs); + }, + + z_increment: function(event) { + this.models.forEach(function(m){ + var newZ = {}; + if (m.get('z_projection')) { + newZ.z_start = m.get('z_start') + 1; + newZ.z_end = m.get('z_end') + 1; + } else { + newZ.theZ = m.get('theZ') + 1; + } + m.set(newZ, {'validate': true}); + }); + return false; + }, + z_decrement: function(event) { + this.models.forEach(function(m){ + var newZ = {}; + if (m.get('z_projection')) { + newZ.z_start = m.get('z_start') - 1; + newZ.z_end = m.get('z_end') - 1; + } else { + newZ.theZ = m.get('theZ') - 1; + } + m.set(newZ, {'validate': true}); + }); + return false; + }, + time_increment: function(event) { + this.models.forEach(function(m){ + m.set({'theT': m.get('theT') + 1}, {'validate': true}); + }); + return false; + }, + time_decrement: function(event) { + this.models.forEach(function(m){ + m.set({'theT': m.get('theT') - 1}, {'validate': true}); + }); + return false; + }, + + render_z_t_labels: function() { + // Rendering Z/T labels without rendering whole component + // means we don't re-build z/t sliders (they don't lose focus) + var sizeZ = this.models.getIfEqual('sizeZ'); + var sizeT = this.models.getIfEqual('sizeT'); + var theT = this.models.getAverage('theT'); + var theZ = this.models.getAverage('theZ'); + var z_projection = this.models.allTrue('z_projection'); + var deltaT = this.models.getDeltaTIfEqual(); + + var Z_disabled = false, + Z_max = sizeZ; + if (!sizeZ || sizeZ === 1) { // undefined or 1 + Z_disabled = true; + Z_max = 1; + $("#vp_z_slider", this.$el).hide(); + } else { + $("#vp_z_slider", this.$el).show(); + } + + var T_disabled = false; + var T_slider_max = this.models.getMin('sizeT'); + if (T_slider_max === 1) { + T_disabled = true; + $("#vp_t_slider", this.$el).hide(); + } else { + $("#vp_t_slider", this.$el).show(); + } + var z_label = theZ + 1; + $("#vp_z_slider input[type='range']", this.$el).val(theZ + 1) + if (z_projection) { + var z_start = Math.round(this.models.getAverage('z_start')); + var z_end = Math.round(this.models.getAverage('z_end')); + z_label = (z_start + 1) + "-" + (z_end + 1); + $("#vp_z_slider .z_end", this.$el).val(z_end + 1); + $("#vp_z_slider .z_start", this.$el).val(z_start + 1); + } else if (!this.models.allEqual('theZ')) { + z_label = "-"; + } + // $("#vp_z_slider").slider({'value': theZ + 1}); + $("#vp_z_value", this.$el).text(z_label + "/" + (sizeZ || '-')); + + var t_label = theT + 1; + var dt_label; + if (!this.models.allEqual('theT')) { + t_label = "-"; + } + $("#vp_t_slider input[type='range']", this.$el).val(theT + 1) + $("#vp_t_value", this.$el).show().text(t_label + "/" + (sizeT || '-')); + + if ((deltaT === 0 || deltaT) && sizeT > 1) { + dt_label = this.formatTime(deltaT); + } else { + dt_label = ""; + } + $("#vp_deltaT", this.$el).text(dt_label); + }, + + render() { + var theZ = this.models.getAverage('theZ'), + z_start = Math.round(this.models.getAverage('z_start')), + z_end = Math.round(this.models.getAverage('z_end')), + theT = this.models.getAverage('theT'), + sizeZ = this.models.getIfEqual('sizeZ'), + sizeT = this.models.getIfEqual('sizeT'), + deltaT = this.models.getDeltaTIfEqual(), + z_projection = this.models.allTrue('z_projection'), + dt_label = ""; + + // cache some values, so we don't need to re-calculate on slide...\ + this.sizeZ = sizeZ; + this.sizeT = sizeT; + + // update sliders + var Z_disabled = false; + var Z_max = sizeZ; + if (!sizeZ || sizeZ === 1) { // undefined or 1 + Z_disabled = true; + Z_max = 1; + } + + // T-slider should be enabled even if we have a mixture of sizeT values. + // Slider T_max is the minimum of sizeT values + // Slider value is average of theT values (but smaller than T_max) + var T_slider_max = this.models.getMin('sizeT'); + console.log("T_slider_max", T_slider_max); + var T_disabled = (T_slider_max === 1); + if ((deltaT === 0 || deltaT) && sizeT > 1) { + dt_label = this.formatTime(deltaT); + } + var t_slider_value = Math.min(theT, T_slider_max); + + var t_label = theT + 1; + var dt_label; + if (!this.models.allEqual('theT')) { + t_label = "-"; + } + + var z_label = theZ + 1; + if (z_projection) { + theZ = z_start; + z_label = (z_start + 1) + "-" + (z_end + 1); + } else if (!this.models.allEqual('theZ')) { + z_label = "-"; + } + + this.$el.html(`
Z
+
${ z_label }/${ (sizeZ || '-') }
+
T
+
${ t_label }/${ (sizeT || '-') }
+
${dt_label}
+ +
+ + + + +
+ +
+ + + +
`); + + // this.render_z_t_labels(); + } +}); + +export default ZtSlidersView; diff --git a/src/templates/viewport.template.html b/src/templates/viewport.template.html index ee165467e..8f7b21ad0 100644 --- a/src/templates/viewport.template.html +++ b/src/templates/viewport.template.html @@ -1,9 +1,5 @@ -
Z
-
-
T
-
-
+
<%= inner_template({imgs_css:imgs_css, opacity:opacity}) %>