Commit 17c52617f831a8b117dba01261bb7b1b4d8fd351
1 parent
b500e561
Exists in
master
and in
2 other branches
Make the orbits plot dynamic. (finally!)
Showing
4 changed files
with
161 additions
and
64 deletions
Show diff stats
VERSION
config.yml
web/static/js/swapp.js
... | ... | @@ -132,22 +132,29 @@ |
132 | 132 | return results$; |
133 | 133 | }; |
134 | 134 | SpaceWeather.prototype.enableTarget = function(target_slug){ |
135 | - var this$ = this; | |
135 | + var ref$, this$ = this; | |
136 | 136 | this.time_series.forEach(function(ts){ |
137 | 137 | if (ts.target.slug === target_slug && this$.parameters[ts.parameter].active) { |
138 | 138 | return ts.show(); |
139 | 139 | } |
140 | 140 | }); |
141 | 141 | this.targets[target_slug].active = true; |
142 | + if ((ref$ = this.orbits) != null) { | |
143 | + ref$.showOrbiter(target_slug); | |
144 | + } | |
142 | 145 | return this; |
143 | 146 | }; |
144 | 147 | SpaceWeather.prototype.disableTarget = function(target_slug){ |
148 | + var ref$; | |
145 | 149 | this.time_series.forEach(function(ts){ |
146 | 150 | if (ts.target.slug === target_slug) { |
147 | 151 | return ts.hide(); |
148 | 152 | } |
149 | 153 | }); |
150 | 154 | this.targets[target_slug].active = false; |
155 | + if ((ref$ = this.orbits) != null) { | |
156 | + ref$.hideOrbiter(target_slug); | |
157 | + } | |
151 | 158 | return this; |
152 | 159 | }; |
153 | 160 | SpaceWeather.prototype.resize = function(){ |
... | ... | @@ -706,9 +713,10 @@ |
706 | 713 | this.data = {}; |
707 | 714 | this.orbiters = {}; |
708 | 715 | this.orbitersElements = {}; |
709 | - this.extremum = 1; | |
710 | - this.xScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]); | |
711 | - this.yScale = d3.scaleLinear().domain([this.extremum, -1 * this.extremum]); | |
716 | + this.orbitersExtrema = {}; | |
717 | + this.lastOrbiterData = {}; | |
718 | + this.xScale = d3.scaleLinear().domain([-1, 1]); | |
719 | + this.yScale = d3.scaleLinear().domain([1, -1]); | |
712 | 720 | this.xAxis = d3.axisBottom().ticks(10); |
713 | 721 | this.yAxis = d3.axisLeft().ticks(10); |
714 | 722 | this.svg = d3.select(this.container).append('svg'); |
... | ... | @@ -738,11 +746,6 @@ |
738 | 746 | if (slug in this.orbitersElements) { |
739 | 747 | throw new Error("Second init of " + slug); |
740 | 748 | } |
741 | - this.extremum = Math.max(this.extremum, 1.11 * d3.max(data, function(d){ | |
742 | - return Math.max(Math.abs(d.x), Math.abs(d.y)); | |
743 | - })); | |
744 | - this.xScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]); | |
745 | - this.yScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]); | |
746 | 749 | orbit_ellipse = this.plotWrapper.append("svg:ellipse").classed('orbit orbit_ellipse', true); |
747 | 750 | orbiter = this.plotWrapper.append("svg:image").attr('xlink:href', config['img']).attr('width', '32px').attr('height', '32px'); |
748 | 751 | orbiter.append('svg:title').text(config.name); |
... | ... | @@ -752,65 +755,122 @@ |
752 | 755 | return this$.yScale(d.x); |
753 | 756 | }); |
754 | 757 | orbit_section = this.plotWrapper.append('path').datum(data).classed('orbit orbit_section', true); |
755 | - this.orbiters[slug] = config; | |
756 | 758 | this.data[slug] = data; |
759 | + this.orbiters[slug] = config; | |
757 | 760 | this.orbitersElements[slug] = { |
758 | 761 | orbiter: orbiter, |
759 | 762 | orbit_ellipse: orbit_ellipse, |
760 | 763 | orbit_section: orbit_section, |
761 | 764 | orbit_line: orbit_line |
762 | 765 | }; |
763 | - this.resize(); | |
766 | + this.orbitersExtrema[slug] = d3.max(data, function(d){ | |
767 | + return Math.max(Math.abs(d.x), Math.abs(d.y)); | |
768 | + }); | |
764 | 769 | $(this.svg.node()).show(); |
770 | + this.resize(true); | |
765 | 771 | return this; |
766 | 772 | }; |
773 | + Orbits.prototype.showOrbiter = function(slug){ | |
774 | + this.orbiters[slug].hidden = false; | |
775 | + this.orbitersElements[slug].orbiter.style("display", null); | |
776 | + this.orbitersElements[slug].orbit_ellipse.style("display", null); | |
777 | + this.orbitersElements[slug].orbit_section.style("display", null); | |
778 | + return this.resize(true); | |
779 | + }; | |
780 | + Orbits.prototype.hideOrbiter = function(slug){ | |
781 | + this.orbiters[slug].hidden = true; | |
782 | + this.orbitersElements[slug].orbiter.style("display", "none"); | |
783 | + this.orbitersElements[slug].orbit_ellipse.style("display", "none"); | |
784 | + this.orbitersElements[slug].orbit_section.style("display", "none"); | |
785 | + return this.resize(true); | |
786 | + }; | |
767 | 787 | Orbits.prototype.clear = function(){ |
768 | 788 | return $(this.svg.node()).remove(); |
769 | 789 | }; |
770 | - Orbits.prototype.resize = function(){ | |
771 | - var width, height, slug, ref$, config; | |
790 | + Orbits.prototype.resize = function(animate){ | |
791 | + var width, height, extremum, s, o, slug, ref$, config, t, t1, this$ = this; | |
792 | + animate == null && (animate = false); | |
772 | 793 | width = Math.ceil($(this.container).width() - this.margin.left - this.margin.right); |
773 | 794 | height = Math.ceil(1.0 * width); |
774 | 795 | console.debug("Resizing orbits : " + width + " × " + height + "…"); |
796 | + extremum = 1.1 * d3.max((function(){ | |
797 | + var ref$, results$ = []; | |
798 | + for (s in ref$ = this.orbiters) { | |
799 | + o = ref$[s]; | |
800 | + if (!o.hidden) { | |
801 | + results$.push(s); | |
802 | + } | |
803 | + } | |
804 | + return results$; | |
805 | + }.call(this)), function(d){ | |
806 | + return this$.orbitersExtrema[d]; | |
807 | + }); | |
808 | + this.xScale = d3.scaleLinear().domain([-1 * extremum, extremum]); | |
809 | + this.yScale = d3.scaleLinear().domain([extremum, -1 * extremum]); | |
775 | 810 | this.xScale.range([0, width]); |
776 | - this.yScale.range([0, height]); | |
811 | + this.yScale.range([height, 0]); | |
777 | 812 | this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom); |
778 | 813 | this.sun.attr("x", width / 2 - 16).attr("y", height / 2 - 16); |
779 | 814 | for (slug in ref$ = this.orbiters) { |
780 | 815 | config = ref$[slug]; |
781 | - this.resizeOrbiter(slug, config, width, height); | |
816 | + this.resizeOrbiter(slug, config, width, height, animate); | |
782 | 817 | } |
783 | 818 | this.xAxis.scale(this.xScale); |
784 | 819 | this.yAxis.scale(this.yScale); |
785 | - this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')').call(this.xAxis); | |
786 | - this.svg.select('.y.axis').call(this.yAxis); | |
820 | + this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')'); | |
821 | + if (animate) { | |
822 | + t = this.svg.transition().duration(750); | |
823 | + t1 = this.svg.transition().duration(4750); | |
824 | + this.svg.select('.x.axis').transition(t).call(this.xAxis); | |
825 | + this.svg.select('.y.axis').transition(t).call(this.yAxis); | |
826 | + } else { | |
827 | + this.svg.select('.x.axis').call(this.xAxis); | |
828 | + this.svg.select('.y.axis').call(this.yAxis); | |
829 | + } | |
787 | 830 | this.xAxisTitle.attr("x", width / 2).attr("y", 37); |
788 | 831 | this.yAxisTitle.attr("x", -1 * height / 2).attr("y", -30); |
789 | 832 | return this; |
790 | 833 | }; |
791 | - Orbits.prototype.resizeOrbiter = function(slug, config, width, height){ | |
792 | - var el, a, b, c, cx, cy, data; | |
834 | + Orbits.prototype.resizeOrbiter = function(slug, config, width, height, animate){ | |
835 | + var tt, el, orbit_section, t, a, b, c, cx, cy, orbit_ellipse; | |
836 | + animate == null && (animate = false); | |
793 | 837 | console.debug("Resizing orbit of " + slug + "…"); |
838 | + tt = this.svg.transition().duration(750); | |
794 | 839 | el = this.orbitersElements[slug]; |
795 | - el['orbit_section'].attr('d', el['orbit_line']); | |
840 | + orbit_section = el['orbit_section']; | |
841 | + if (animate) { | |
842 | + t = this.svg.transition().duration(750); | |
843 | + orbit_section = orbit_section.transition(tt); | |
844 | + } | |
845 | + orbit_section.attr('d', el['orbit_line']); | |
796 | 846 | a = config['orbit']['a']; |
797 | 847 | b = config['orbit']['b']; |
798 | 848 | c = Math.sqrt(a * a - b * b); |
799 | 849 | cx = width / 2 - c; |
800 | 850 | cy = height / 2; |
801 | - 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)); | |
802 | - data = this.data[slug]; | |
803 | - el['orbiter'].attr('x', this.xScale(data[data.length - 1].y) - 16); | |
804 | - el['orbiter'].attr('y', this.yScale(data[data.length - 1].x) - 16); | |
851 | + orbit_ellipse = el['orbit_ellipse']; | |
852 | + if (animate) { | |
853 | + t = this.svg.transition().duration(750); | |
854 | + orbit_ellipse = orbit_ellipse.transition(t); | |
855 | + } | |
856 | + orbit_ellipse.attr('cx', cx).attr('cy', cy).attr('rx', this.xScale(a) - this.xScale(0)).attr('ry', this.yScale(b) - this.yScale(0)); | |
857 | + this.repositionOrbiter(slug, null, true); | |
805 | 858 | return this; |
806 | 859 | }; |
807 | - Orbits.prototype.repositionOrbiter = function(slug, datum){ | |
808 | - var data, el; | |
860 | + Orbits.prototype.repositionOrbiter = function(slug, datum, animate){ | |
861 | + var data, el, t; | |
862 | + animate == null && (animate = false); | |
809 | 863 | data = this.data[slug]; |
864 | + datum == null && (datum = this.lastOrbiterData[slug]); | |
810 | 865 | datum == null && (datum = data[data.length - 1]); |
811 | - el = this.orbitersElements[slug]; | |
812 | - el['orbiter'].attr('x', this.xScale(datum.y) - 16); | |
813 | - el['orbiter'].attr('y', this.yScale(datum.x) - 16); | |
866 | + this.lastOrbiterData[slug] = datum; | |
867 | + el = this.orbitersElements[slug]['orbiter']; | |
868 | + if (animate) { | |
869 | + t = this.svg.transition().duration(750); | |
870 | + el = el.transition(t); | |
871 | + } | |
872 | + el.attr('x', this.xScale(datum.y) - 16); | |
873 | + el.attr('y', this.yScale(datum.x) - 16); | |
814 | 874 | return this; |
815 | 875 | }; |
816 | 876 | Orbits.prototype.bisectDate = d3.bisector(function(d){ | ... | ... |
web/static/js/swapp.ls
... | ... | @@ -128,11 +128,13 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE |
128 | 128 | enableTarget: (target_slug) -> |
129 | 129 | @time_series.forEach((ts) ~> ts.show() if ts.target.slug == target_slug && @parameters[ts.parameter].active) |
130 | 130 | @targets[target_slug].active = true |
131 | + @orbits?.showOrbiter target_slug | |
131 | 132 | this |
132 | 133 | |
133 | 134 | disableTarget: (target_slug) -> |
134 | 135 | @time_series.forEach((ts) -> ts.hide() if ts.target.slug == target_slug) |
135 | 136 | @targets[target_slug].active = false |
137 | + @orbits?.hideOrbiter target_slug | |
136 | 138 | this |
137 | 139 | |
138 | 140 | resize: -> |
... | ... | @@ -656,10 +658,11 @@ export class Orbits |
656 | 658 | |
657 | 659 | @data = {} # slug => HEE array |
658 | 660 | @orbiters = {} # slug => config |
659 | - @orbitersElements = {} | |
660 | - @extremum = 1 | |
661 | - @xScale = d3.scaleLinear().domain([-1 * @extremum, @extremum]) | |
662 | - @yScale = d3.scaleLinear().domain([@extremum, -1 * @extremum]) | |
661 | + @orbitersElements = {} # see initOrbiter | |
662 | + @orbitersExtrema = {} # slug => local extrema | |
663 | + @lastOrbiterData = {} # slug => most recently used datum for position | |
664 | + @xScale = d3.scaleLinear().domain([-1, 1]) | |
665 | + @yScale = d3.scaleLinear().domain([1, -1]) | |
663 | 666 | |
664 | 667 | @xAxis = d3.axisBottom().ticks(10) |
665 | 668 | @yAxis = d3.axisLeft().ticks(10) |
... | ... | @@ -700,12 +703,6 @@ export class Orbits |
700 | 703 | console.info "Initializing orbit of #{config.name}…" |
701 | 704 | if slug of @orbitersElements then throw new Error("Second init of #{slug}") |
702 | 705 | |
703 | - @extremum = Math.max(@extremum, 1.11 * d3.max(data, (d) -> | |
704 | - Math.max(Math.abs(d.x), Math.abs(d.y)) | |
705 | - )) | |
706 | - @xScale = d3.scaleLinear().domain([-1 * @extremum, @extremum]) | |
707 | - @yScale = d3.scaleLinear().domain([-1 * @extremum, @extremum]) | |
708 | - | |
709 | 706 | # The order is important, as it will define the default z-order |
710 | 707 | orbit_ellipse = @plotWrapper.append("svg:ellipse") |
711 | 708 | .classed('orbit orbit_ellipse', true) |
... | ... | @@ -722,31 +719,54 @@ export class Orbits |
722 | 719 | .datum(data) |
723 | 720 | .classed('orbit orbit_section', true) |
724 | 721 | |
725 | - @orbiters[slug] = config | |
726 | 722 | @data[slug] = data |
723 | + @orbiters[slug] = config | |
727 | 724 | @orbitersElements[slug] = |
728 | 725 | orbiter: orbiter |
729 | 726 | orbit_ellipse: orbit_ellipse |
730 | 727 | orbit_section: orbit_section |
731 | 728 | orbit_line: orbit_line |
732 | - | |
733 | - @resize() | |
729 | + @orbitersExtrema[slug] = d3.max(data, (d) -> | |
730 | + Math.max(Math.abs(d.x), Math.abs(d.y)) | |
731 | + ) | |
734 | 732 | |
735 | 733 | $(@svg.node()).show(); |
736 | 734 | |
735 | + @resize(true) | |
736 | + | |
737 | 737 | this |
738 | 738 | |
739 | + showOrbiter: (slug) -> | |
740 | + @orbiters[slug].hidden = false | |
741 | + @orbitersElements[slug].orbiter.style("display", null) | |
742 | + @orbitersElements[slug].orbit_ellipse.style("display", null) | |
743 | + @orbitersElements[slug].orbit_section.style("display", null) | |
744 | + @resize(true) | |
745 | + | |
746 | + hideOrbiter: (slug) -> | |
747 | + @orbiters[slug].hidden = true | |
748 | + @orbitersElements[slug].orbiter.style("display", "none") | |
749 | + @orbitersElements[slug].orbit_ellipse.style("display", "none") | |
750 | + @orbitersElements[slug].orbit_section.style("display", "none") | |
751 | + @resize(true) | |
752 | + | |
739 | 753 | clear: -> |
740 | 754 | $(@svg.node()).remove() |
741 | 755 | |
742 | - resize: -> | |
756 | + resize: (animate = false) -> | |
743 | 757 | width = Math.ceil($(@container).width() - @margin.left - @margin.right) |
744 | 758 | height = Math.ceil(1.0 * width) |
745 | 759 | |
746 | 760 | console.debug("Resizing orbits : #{width} × #{height}…") |
747 | 761 | |
762 | + extremum = 1.1 * d3.max([s for s, o of @orbiters when not o.hidden], (d) ~> | |
763 | + @orbitersExtrema[d] | |
764 | + ) | |
765 | + @xScale = d3.scaleLinear().domain([-1 * extremum, extremum]) | |
766 | + @yScale = d3.scaleLinear().domain([extremum, -1 * extremum]) | |
767 | + | |
748 | 768 | @xScale.range([0, width]) |
749 | - @yScale.range([0, height]) | |
769 | + @yScale.range([height, 0]) | |
750 | 770 | |
751 | 771 | @svg.attr('width', width + @margin.right + @margin.left) |
752 | 772 | .attr('height', height + @margin.top + @margin.bottom) |
... | ... | @@ -754,17 +774,20 @@ export class Orbits |
754 | 774 | @sun.attr("x", width / 2 - 16).attr("y", height / 2 - 16) |
755 | 775 | |
756 | 776 | for slug, config of @orbiters |
757 | - @resizeOrbiter(slug, config, width, height) | |
777 | + @resizeOrbiter(slug, config, width, height, animate) | |
758 | 778 | |
759 | 779 | @xAxis.scale(@xScale) |
760 | 780 | @yAxis.scale(@yScale) |
761 | 781 | |
762 | - @svg.select('.x.axis') | |
763 | - .attr('transform', 'translate(0,' + height + ')') | |
764 | - .call(@xAxis) | |
765 | - | |
766 | - @svg.select('.y.axis') | |
767 | - .call(@yAxis) | |
782 | + @svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')') | |
783 | + if animate | |
784 | + t = @svg.transition().duration(750) | |
785 | + t1 = @svg.transition().duration(4750) | |
786 | + @svg.select('.x.axis').transition(t).call(@xAxis); | |
787 | + @svg.select('.y.axis').transition(t).call(@yAxis); | |
788 | + else | |
789 | + @svg.select('.x.axis').call(@xAxis) | |
790 | + @svg.select('.y.axis').call(@yAxis) | |
768 | 791 | |
769 | 792 | @xAxisTitle.attr("x", width / 2) |
770 | 793 | .attr("y", 37) |
... | ... | @@ -773,35 +796,49 @@ export class Orbits |
773 | 796 | |
774 | 797 | this |
775 | 798 | |
776 | - resizeOrbiter: (slug, config, width, height) -> | |
799 | + resizeOrbiter: (slug, config, width, height, animate = false) -> | |
777 | 800 | console.debug("Resizing orbit of #{slug}…") |
778 | 801 | |
802 | + tt = @svg.transition().duration(750) | |
779 | 803 | el = @orbitersElements[slug] |
780 | - el['orbit_section'].attr('d', el['orbit_line']) | |
804 | + orbit_section = el['orbit_section'] | |
805 | + if animate | |
806 | + t = @svg.transition().duration(750) | |
807 | + orbit_section = orbit_section.transition(tt) | |
808 | + orbit_section.attr('d', el['orbit_line']) | |
781 | 809 | |
782 | 810 | a = config['orbit']['a'] |
783 | 811 | b = config['orbit']['b'] |
784 | 812 | c = Math.sqrt(a*a - b*b) |
785 | 813 | cx = (width / 2) - c |
786 | 814 | cy = (height / 2) |
787 | - el['orbit_ellipse'].attr('cx', cx).attr('cy', cy) | |
815 | + | |
816 | + orbit_ellipse = el['orbit_ellipse'] | |
817 | + if animate | |
818 | + t = @svg.transition().duration(750) | |
819 | + orbit_ellipse = orbit_ellipse.transition(t) | |
820 | + # These ellipses ain't worth much | |
821 | + # Maybe a simple circle whose radius is the mean radius of the orbit ? | |
822 | + orbit_ellipse.attr('cx', cx).attr('cy', cy) | |
788 | 823 | .attr('rx', @xScale(a) - @xScale(0)) |
789 | 824 | .attr('ry', @yScale(b) - @yScale(0)) |
790 | 825 | # .attr('transform', 'rotate(66,'+(cx+c)+', '+cy+')') |
791 | 826 | |
792 | - data = @data[slug] | |
793 | - | |
794 | - el['orbiter'].attr('x', @xScale(data[data.length - 1].y) - 16) | |
795 | - el['orbiter'].attr('y', @yScale(data[data.length - 1].x) - 16) | |
827 | + @repositionOrbiter(slug, null, true) | |
796 | 828 | |
797 | 829 | this |
798 | 830 | |
799 | - repositionOrbiter: (slug, datum) -> | |
831 | + repositionOrbiter: (slug, datum, animate = false) -> | |
800 | 832 | data = @data[slug] |
833 | + datum ?= @lastOrbiterData[slug] | |
801 | 834 | datum ?= data[data.length - 1] |
802 | - el = @orbitersElements[slug] | |
803 | - el['orbiter'].attr('x', @xScale(datum.y) - 16) | |
804 | - el['orbiter'].attr('y', @yScale(datum.x) - 16) | |
835 | + @lastOrbiterData[slug] = datum | |
836 | + el = @orbitersElements[slug]['orbiter'] | |
837 | + if animate | |
838 | + t = @svg.transition().duration(750) | |
839 | + el = el.transition(t) | |
840 | + el.attr('x', @xScale(datum.y) - 16) | |
841 | + el.attr('y', @yScale(datum.x) - 16) | |
805 | 842 | this |
806 | 843 | |
807 | 844 | bisectDate: d3.bisector((d) -> d.t).left | ... | ... |