diff --git a/VERSION b/VERSION index 537aabf..0fab938 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0-beta \ No newline at end of file +1.0.0-rc3 \ No newline at end of file diff --git a/config.yml b/config.yml index a5fcb7e..2e31ee9 100644 --- a/config.yml +++ b/config.yml @@ -102,7 +102,7 @@ targets: - type: 'planet' slug: 'earth' name: 'Earth' - title: 'Earth' + title: 'Earth (Coming NEXT!)' orbit: models: - slug: 'earth_orb_all' diff --git a/web/static/js/swapp.js b/web/static/js/swapp.js index 6fb8728..e2d78aa 100644 --- a/web/static/js/swapp.js +++ b/web/static/js/swapp.js @@ -132,22 +132,29 @@ return results$; }; SpaceWeather.prototype.enableTarget = function(target_slug){ - var this$ = this; + var ref$, this$ = this; this.time_series.forEach(function(ts){ if (ts.target.slug === target_slug && this$.parameters[ts.parameter].active) { return ts.show(); } }); this.targets[target_slug].active = true; + if ((ref$ = this.orbits) != null) { + ref$.showOrbiter(target_slug); + } return this; }; SpaceWeather.prototype.disableTarget = function(target_slug){ + var ref$; this.time_series.forEach(function(ts){ if (ts.target.slug === target_slug) { return ts.hide(); } }); this.targets[target_slug].active = false; + if ((ref$ = this.orbits) != null) { + ref$.hideOrbiter(target_slug); + } return this; }; SpaceWeather.prototype.resize = function(){ @@ -706,9 +713,10 @@ this.data = {}; this.orbiters = {}; this.orbitersElements = {}; - this.extremum = 1; - this.xScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]); - this.yScale = d3.scaleLinear().domain([this.extremum, -1 * this.extremum]); + this.orbitersExtrema = {}; + this.lastOrbiterData = {}; + this.xScale = d3.scaleLinear().domain([-1, 1]); + this.yScale = d3.scaleLinear().domain([1, -1]); this.xAxis = d3.axisBottom().ticks(10); this.yAxis = d3.axisLeft().ticks(10); this.svg = d3.select(this.container).append('svg'); @@ -738,11 +746,6 @@ if (slug in this.orbitersElements) { throw new Error("Second init of " + slug); } - this.extremum = Math.max(this.extremum, 1.11 * d3.max(data, function(d){ - return Math.max(Math.abs(d.x), Math.abs(d.y)); - })); - this.xScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]); - this.yScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]); orbit_ellipse = this.plotWrapper.append("svg:ellipse").classed('orbit orbit_ellipse', true); orbiter = this.plotWrapper.append("svg:image").attr('xlink:href', config['img']).attr('width', '32px').attr('height', '32px'); orbiter.append('svg:title').text(config.name); @@ -752,65 +755,122 @@ return this$.yScale(d.x); }); orbit_section = this.plotWrapper.append('path').datum(data).classed('orbit orbit_section', true); - this.orbiters[slug] = config; this.data[slug] = data; + this.orbiters[slug] = config; this.orbitersElements[slug] = { orbiter: orbiter, orbit_ellipse: orbit_ellipse, orbit_section: orbit_section, orbit_line: orbit_line }; - this.resize(); + this.orbitersExtrema[slug] = d3.max(data, function(d){ + return Math.max(Math.abs(d.x), Math.abs(d.y)); + }); $(this.svg.node()).show(); + this.resize(true); return this; }; + Orbits.prototype.showOrbiter = function(slug){ + this.orbiters[slug].hidden = false; + this.orbitersElements[slug].orbiter.style("display", null); + this.orbitersElements[slug].orbit_ellipse.style("display", null); + this.orbitersElements[slug].orbit_section.style("display", null); + return this.resize(true); + }; + Orbits.prototype.hideOrbiter = function(slug){ + this.orbiters[slug].hidden = true; + this.orbitersElements[slug].orbiter.style("display", "none"); + this.orbitersElements[slug].orbit_ellipse.style("display", "none"); + this.orbitersElements[slug].orbit_section.style("display", "none"); + return this.resize(true); + }; Orbits.prototype.clear = function(){ return $(this.svg.node()).remove(); }; - Orbits.prototype.resize = function(){ - var width, height, slug, ref$, config; + Orbits.prototype.resize = function(animate){ + var width, height, extremum, s, o, slug, ref$, config, t, t1, this$ = this; + animate == null && (animate = false); width = Math.ceil($(this.container).width() - this.margin.left - this.margin.right); height = Math.ceil(1.0 * width); console.debug("Resizing orbits : " + width + " × " + height + "…"); + extremum = 1.1 * d3.max((function(){ + var ref$, results$ = []; + for (s in ref$ = this.orbiters) { + o = ref$[s]; + if (!o.hidden) { + results$.push(s); + } + } + return results$; + }.call(this)), function(d){ + return this$.orbitersExtrema[d]; + }); + this.xScale = d3.scaleLinear().domain([-1 * extremum, extremum]); + this.yScale = d3.scaleLinear().domain([extremum, -1 * extremum]); this.xScale.range([0, width]); - this.yScale.range([0, height]); + this.yScale.range([height, 0]); this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom); this.sun.attr("x", width / 2 - 16).attr("y", height / 2 - 16); for (slug in ref$ = this.orbiters) { config = ref$[slug]; - this.resizeOrbiter(slug, config, width, height); + this.resizeOrbiter(slug, config, width, height, animate); } this.xAxis.scale(this.xScale); this.yAxis.scale(this.yScale); - this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')').call(this.xAxis); - this.svg.select('.y.axis').call(this.yAxis); + this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')'); + if (animate) { + t = this.svg.transition().duration(750); + t1 = this.svg.transition().duration(4750); + this.svg.select('.x.axis').transition(t).call(this.xAxis); + this.svg.select('.y.axis').transition(t).call(this.yAxis); + } else { + this.svg.select('.x.axis').call(this.xAxis); + this.svg.select('.y.axis').call(this.yAxis); + } this.xAxisTitle.attr("x", width / 2).attr("y", 37); this.yAxisTitle.attr("x", -1 * height / 2).attr("y", -30); return this; }; - Orbits.prototype.resizeOrbiter = function(slug, config, width, height){ - var el, a, b, c, cx, cy, data; + Orbits.prototype.resizeOrbiter = function(slug, config, width, height, animate){ + var tt, el, orbit_section, t, a, b, c, cx, cy, orbit_ellipse; + animate == null && (animate = false); console.debug("Resizing orbit of " + slug + "…"); + tt = this.svg.transition().duration(750); el = this.orbitersElements[slug]; - el['orbit_section'].attr('d', el['orbit_line']); + orbit_section = el['orbit_section']; + if (animate) { + t = this.svg.transition().duration(750); + orbit_section = orbit_section.transition(tt); + } + orbit_section.attr('d', el['orbit_line']); a = config['orbit']['a']; b = config['orbit']['b']; c = Math.sqrt(a * a - b * b); cx = width / 2 - c; cy = height / 2; - el['orbit_ellipse'].attr('cx', cx).attr('cy', cy).attr('rx', this.xScale(a) - this.xScale(0)).attr('ry', this.yScale(b) - this.yScale(0)); - data = this.data[slug]; - el['orbiter'].attr('x', this.xScale(data[data.length - 1].y) - 16); - el['orbiter'].attr('y', this.yScale(data[data.length - 1].x) - 16); + orbit_ellipse = el['orbit_ellipse']; + if (animate) { + t = this.svg.transition().duration(750); + orbit_ellipse = orbit_ellipse.transition(t); + } + orbit_ellipse.attr('cx', cx).attr('cy', cy).attr('rx', this.xScale(a) - this.xScale(0)).attr('ry', this.yScale(b) - this.yScale(0)); + this.repositionOrbiter(slug, null, true); return this; }; - Orbits.prototype.repositionOrbiter = function(slug, datum){ - var data, el; + Orbits.prototype.repositionOrbiter = function(slug, datum, animate){ + var data, el, t; + animate == null && (animate = false); data = this.data[slug]; + datum == null && (datum = this.lastOrbiterData[slug]); datum == null && (datum = data[data.length - 1]); - el = this.orbitersElements[slug]; - el['orbiter'].attr('x', this.xScale(datum.y) - 16); - el['orbiter'].attr('y', this.yScale(datum.x) - 16); + this.lastOrbiterData[slug] = datum; + el = this.orbitersElements[slug]['orbiter']; + if (animate) { + t = this.svg.transition().duration(750); + el = el.transition(t); + } + el.attr('x', this.xScale(datum.y) - 16); + el.attr('y', this.yScale(datum.x) - 16); return this; }; Orbits.prototype.bisectDate = d3.bisector(function(d){ diff --git a/web/static/js/swapp.ls b/web/static/js/swapp.ls index 668b8ce..f288985 100644 --- a/web/static/js/swapp.ls +++ b/web/static/js/swapp.ls @@ -128,11 +128,13 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE enableTarget: (target_slug) -> @time_series.forEach((ts) ~> ts.show() if ts.target.slug == target_slug && @parameters[ts.parameter].active) @targets[target_slug].active = true + @orbits?.showOrbiter target_slug this disableTarget: (target_slug) -> @time_series.forEach((ts) -> ts.hide() if ts.target.slug == target_slug) @targets[target_slug].active = false + @orbits?.hideOrbiter target_slug this resize: -> @@ -656,10 +658,11 @@ export class Orbits @data = {} # slug => HEE array @orbiters = {} # slug => config - @orbitersElements = {} - @extremum = 1 - @xScale = d3.scaleLinear().domain([-1 * @extremum, @extremum]) - @yScale = d3.scaleLinear().domain([@extremum, -1 * @extremum]) + @orbitersElements = {} # see initOrbiter + @orbitersExtrema = {} # slug => local extrema + @lastOrbiterData = {} # slug => most recently used datum for position + @xScale = d3.scaleLinear().domain([-1, 1]) + @yScale = d3.scaleLinear().domain([1, -1]) @xAxis = d3.axisBottom().ticks(10) @yAxis = d3.axisLeft().ticks(10) @@ -700,12 +703,6 @@ export class Orbits console.info "Initializing orbit of #{config.name}…" if slug of @orbitersElements then throw new Error("Second init of #{slug}") - @extremum = Math.max(@extremum, 1.11 * d3.max(data, (d) -> - Math.max(Math.abs(d.x), Math.abs(d.y)) - )) - @xScale = d3.scaleLinear().domain([-1 * @extremum, @extremum]) - @yScale = d3.scaleLinear().domain([-1 * @extremum, @extremum]) - # The order is important, as it will define the default z-order orbit_ellipse = @plotWrapper.append("svg:ellipse") .classed('orbit orbit_ellipse', true) @@ -722,31 +719,54 @@ export class Orbits .datum(data) .classed('orbit orbit_section', true) - @orbiters[slug] = config @data[slug] = data + @orbiters[slug] = config @orbitersElements[slug] = orbiter: orbiter orbit_ellipse: orbit_ellipse orbit_section: orbit_section orbit_line: orbit_line - - @resize() + @orbitersExtrema[slug] = d3.max(data, (d) -> + Math.max(Math.abs(d.x), Math.abs(d.y)) + ) $(@svg.node()).show(); + @resize(true) + this + showOrbiter: (slug) -> + @orbiters[slug].hidden = false + @orbitersElements[slug].orbiter.style("display", null) + @orbitersElements[slug].orbit_ellipse.style("display", null) + @orbitersElements[slug].orbit_section.style("display", null) + @resize(true) + + hideOrbiter: (slug) -> + @orbiters[slug].hidden = true + @orbitersElements[slug].orbiter.style("display", "none") + @orbitersElements[slug].orbit_ellipse.style("display", "none") + @orbitersElements[slug].orbit_section.style("display", "none") + @resize(true) + clear: -> $(@svg.node()).remove() - resize: -> + resize: (animate = false) -> width = Math.ceil($(@container).width() - @margin.left - @margin.right) height = Math.ceil(1.0 * width) console.debug("Resizing orbits : #{width} × #{height}…") + extremum = 1.1 * d3.max([s for s, o of @orbiters when not o.hidden], (d) ~> + @orbitersExtrema[d] + ) + @xScale = d3.scaleLinear().domain([-1 * extremum, extremum]) + @yScale = d3.scaleLinear().domain([extremum, -1 * extremum]) + @xScale.range([0, width]) - @yScale.range([0, height]) + @yScale.range([height, 0]) @svg.attr('width', width + @margin.right + @margin.left) .attr('height', height + @margin.top + @margin.bottom) @@ -754,17 +774,20 @@ export class Orbits @sun.attr("x", width / 2 - 16).attr("y", height / 2 - 16) for slug, config of @orbiters - @resizeOrbiter(slug, config, width, height) + @resizeOrbiter(slug, config, width, height, animate) @xAxis.scale(@xScale) @yAxis.scale(@yScale) - @svg.select('.x.axis') - .attr('transform', 'translate(0,' + height + ')') - .call(@xAxis) - - @svg.select('.y.axis') - .call(@yAxis) + @svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')') + if animate + t = @svg.transition().duration(750) + t1 = @svg.transition().duration(4750) + @svg.select('.x.axis').transition(t).call(@xAxis); + @svg.select('.y.axis').transition(t).call(@yAxis); + else + @svg.select('.x.axis').call(@xAxis) + @svg.select('.y.axis').call(@yAxis) @xAxisTitle.attr("x", width / 2) .attr("y", 37) @@ -773,35 +796,49 @@ export class Orbits this - resizeOrbiter: (slug, config, width, height) -> + resizeOrbiter: (slug, config, width, height, animate = false) -> console.debug("Resizing orbit of #{slug}…") + tt = @svg.transition().duration(750) el = @orbitersElements[slug] - el['orbit_section'].attr('d', el['orbit_line']) + orbit_section = el['orbit_section'] + if animate + t = @svg.transition().duration(750) + orbit_section = orbit_section.transition(tt) + orbit_section.attr('d', el['orbit_line']) a = config['orbit']['a'] b = config['orbit']['b'] c = Math.sqrt(a*a - b*b) cx = (width / 2) - c cy = (height / 2) - el['orbit_ellipse'].attr('cx', cx).attr('cy', cy) + + orbit_ellipse = el['orbit_ellipse'] + if animate + t = @svg.transition().duration(750) + orbit_ellipse = orbit_ellipse.transition(t) + # These ellipses ain't worth much + # Maybe a simple circle whose radius is the mean radius of the orbit ? + orbit_ellipse.attr('cx', cx).attr('cy', cy) .attr('rx', @xScale(a) - @xScale(0)) .attr('ry', @yScale(b) - @yScale(0)) # .attr('transform', 'rotate(66,'+(cx+c)+', '+cy+')') - data = @data[slug] - - el['orbiter'].attr('x', @xScale(data[data.length - 1].y) - 16) - el['orbiter'].attr('y', @yScale(data[data.length - 1].x) - 16) + @repositionOrbiter(slug, null, true) this - repositionOrbiter: (slug, datum) -> + repositionOrbiter: (slug, datum, animate = false) -> data = @data[slug] + datum ?= @lastOrbiterData[slug] datum ?= data[data.length - 1] - el = @orbitersElements[slug] - el['orbiter'].attr('x', @xScale(datum.y) - 16) - el['orbiter'].attr('y', @yScale(datum.x) - 16) + @lastOrbiterData[slug] = datum + el = @orbitersElements[slug]['orbiter'] + if animate + t = @svg.transition().duration(750) + el = el.transition(t) + el.attr('x', @xScale(datum.y) - 16) + el.attr('y', @yScale(datum.x) - 16) this bisectDate: d3.bisector((d) -> d.t).left -- libgit2 0.21.2