From b9777221491d93a1ba87db5aa9a7a8b6e69b1c7c Mon Sep 17 00:00:00 2001 From: Connor Stroomberg Date: Thu, 23 Oct 2014 11:58:14 +0200 Subject: [PATCH] Redo scalerange calculations --- app/js/controllers/scaleRange.js | 25 +-------- app/js/directives.js | 9 ++- app/js/services/scaleRangeService.js | 84 ++++++++++++++++++++++++---- test/unit/scaleRangeServiceSpec.js | 62 ++++++++++++++++---- 4 files changed, 130 insertions(+), 50 deletions(-) diff --git a/app/js/controllers/scaleRange.js b/app/js/controllers/scaleRange.js index 8292c6cf2..03dd90bc8 100644 --- a/app/js/controllers/scaleRange.js +++ b/app/js/controllers/scaleRange.js @@ -72,29 +72,8 @@ define(['mcda/config', 'angular', 'underscore'], function(Config, angular, _) { }; // Set scales for slider - var margin = 0.5 * (ScaleRangeService.nice(to) - ScaleRangeService.nice(from)); - var scale = state.problem.criteria[criterion[0]].scale || [null, null]; - scale[0] = _.isNull(scale[0]) ? -Infinity : scale[0]; - scale[1] = _.isNull(scale[1]) ? Infinity : scale[1]; - - var boundFrom = function(val) { - return val < scale[0] ? scale[0] : val; - }; - var boundTo = function(val) { - return val > scale[1] ? scale[1] : val; - }; - scales[criterion[0]] = { - restrictFrom: criterionRange[0], - restrictTo: criterionRange[1], - from: boundFrom(ScaleRangeService.nice(from) - margin), - to: boundTo(ScaleRangeService.nice(to) + margin), - increaseFrom: function() { - this.from = boundFrom(this.from - margin); - }, - increaseTo: function() { - this.to = boundTo(this.to + margin); - } - }; + var criterionScale = state.problem.criteria[criterion[0]].scale; + scales[criterion[0]] = ScaleRangeService.calculateScales(criterionScale, from, to, criterionRange); }); $scope.currentStep = _.extend(state, { diff --git a/app/js/directives.js b/app/js/directives.js index 88e509724..d9d18690f 100644 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -3,7 +3,7 @@ define(['require', 'underscore', 'jQuery', 'angular', 'd3', 'nvd3'], function(re var directives = angular.module('elicit.directives', []); - directives.directive('slider', function() { + directives.directive('slider', function($filter) { var initialize = function(scope, $element) { var type = scope.type; var from = scope.range.from; @@ -25,13 +25,12 @@ define(['require', 'underscore', 'jQuery', 'angular', 'd3', 'nvd3'], function(re --steps; } - var precision = 3; var stepToValue = function(step) { - return (from + (step / steps) * delta).toFixed(precision); + return $filter('number')((from + (step / steps) * delta)); }; function valueToStep(value) { - return ((value - from) / delta * steps).toFixed(precision); + return $filter('number')(((value - from) / delta * steps)); } function getModelValue() { @@ -91,7 +90,7 @@ define(['require', 'underscore', 'jQuery', 'angular', 'd3', 'nvd3'], function(re link: function(scope, $element) { var init = function() { if (scope.range) { - console.log('range from' + scope.range.from + ' to ' + scope.range.to) + console.log('range from' + scope.range.from + ' to ' + scope.range.to); initialize(scope, $element); } }; diff --git a/app/js/services/scaleRangeService.js b/app/js/services/scaleRangeService.js index 8a514398b..f1e6a1f57 100644 --- a/app/js/services/scaleRangeService.js +++ b/app/js/services/scaleRangeService.js @@ -6,21 +6,81 @@ define(['angular'], var ScaleRangeService = function() { - function nice(x) { - var log10 = function(y) { - return Math.log(y) / Math.log(10); - }; - var negative = x < 0; - var absX = Math.abs(x); - var val = Math.pow(10, Math.floor(log10(absX))); - var nice = _.find(_.range(1, 11), function(n) { - return absX <= val * n; - }); - return (negative ? -1 : 1) * (val * nice); + var log10 = function(x) { + return Math.log(x) / Math.log(10); }; + function nice(x, dirFun) { + if(x === 0) return 0; + var absX = Math.abs(x); + var log10X = log10(absX); + var factor; + var normalised; + var ceiled; + var deNormalised; + if (absX >= 1) { + factor = Math.floor(log10X); + normalised = x / Math.pow(10, factor); + ceiled = dirFun(normalised); + deNormalised = ceiled * Math.pow(10, factor); + } else { + factor = Math.ceil(Math.abs(log10X)); + normalised = x * Math.pow(10, factor); + ceiled = dirFun(normalised); + deNormalised = ceiled * Math.pow(10, -factor); + } + + // console.log('when x=' + x + ' log10X = ' + log10X); + // console.log('when x=' + x + ' factor = ' + factor); + // console.log('when x=' + x + ' normalised = ' + normalised); + // console.log('when x=' + x + ' ceiled = ' + ceiled); + // console.log('when x=' + x + ' deNormalised = ' + deNormalised); + + return deNormalised; + } + + function niceTo(x) { + return nice(x, Math.ceil); + } + + function niceFrom(x) { + return nice(x, Math.floor); + } + + + function calculateScales(criterionScale, from, to, criterionRange) { + var + boundFrom = function(val) { + return val < scale[0] ? scale[0] : val; + }, + boundTo = function(val) { + return val > scale[1] ? scale[1] : val; + }, + margin = 0.5 * (to - from), + scale = criterionScale || [null, null]; + + scale[0] = _.isNull(scale[0]) ? -Infinity : scale[0]; + scale[1] = _.isNull(scale[1]) ? Infinity : scale[1]; + + return { + restrictFrom: criterionRange[0], + restrictTo: criterionRange[1], + from: niceFrom(from), + to: niceTo(to), + increaseFrom: function() { + this.from = niceFrom(boundFrom(this.from - margin)); + }, + increaseTo: function() { + this.to = niceTo(boundTo(this.to + margin)); + } + }; + } + return { - nice: nice + nice: nice, + niceTo: niceTo, + niceFrom: niceFrom, + calculateScales: calculateScales }; }; diff --git a/test/unit/scaleRangeServiceSpec.js b/test/unit/scaleRangeServiceSpec.js index fa9aea48f..c9e71885c 100644 --- a/test/unit/scaleRangeServiceSpec.js +++ b/test/unit/scaleRangeServiceSpec.js @@ -1,22 +1,64 @@ define(['angular', 'angular-mocks', 'mcda/services/scaleRangeService'], function() { + + // - the lower bound must be lower than the lower end of the observed range + // - the upper bound should be higher than the upper end of the observed range + // - the values should be "nice" (have no more than two significant digits, preferably only one) + + // - the lower bound must be greater than or equal to the theoretical lower bound + // - the upper bound must be smaller than or equal to the theoretical upper bound + + describe('The scaleRange service', function() { - var a; + beforeEach(module('elicit.scaleRangeService')); - describe('nice', function() { + describe('calculateScales', function() { + it('on unbounded scales, bounds should lie outside the observed range', inject(function(ScaleRangeService) { + var criterionScale = [null, null]; + var from = -16.123; + var to = -12.123; + var criterionRange = [from, to]; - beforeEach(module('elicit.scaleRangeService')); + var result = ScaleRangeService.calculateScales(criterionScale, from, to, criterionRange); + expect(result.from).toEqual(-20); + expect(result.to).toEqual(-10); + })); + }); + + describe('niceFrom', function() { + it('should', inject(function(ScaleRangeService) { + expect(ScaleRangeService.niceFrom(150)).toEqual(100); + expect(ScaleRangeService.niceFrom(15)).toEqual(10); + expect(ScaleRangeService.niceFrom(1.5)).toEqual(1); + expect(ScaleRangeService.niceFrom(0.15)).toEqual(0.1); + expect(ScaleRangeService.niceFrom(0.015)).toEqual(0.01); - beforeEach(function() { + expect(ScaleRangeService.niceFrom(-150)).toEqual(-200); + expect(ScaleRangeService.niceFrom(-15)).toEqual(-20); + expect(ScaleRangeService.niceFrom(-1.5)).toEqual(-2); + expect(ScaleRangeService.niceFrom(-0.15)).toEqual(-0.2); + expect(ScaleRangeService.niceFrom(-0.015)).toEqual(-0.02); - }); + expect(ScaleRangeService.niceFrom(0)).toEqual(0); + + })); + }); + describe('niceTo', function() { + it('should', inject(function(ScaleRangeService) { + expect(ScaleRangeService.niceTo(150)).toEqual(200); + expect(ScaleRangeService.niceTo(15)).toEqual(20); + expect(ScaleRangeService.niceTo(1.5)).toEqual(2); + expect(ScaleRangeService.niceTo(0.15)).toEqual(0.2); + expect(ScaleRangeService.niceTo(0.015)).toEqual(0.02); - it('should do something', - inject(function($rootScope, ScaleRangeService) { - - expect(ScaleRangeService.nice(-16)).toEqual(-20); - })); + expect(ScaleRangeService.niceTo(-150)).toEqual(-100); + expect(ScaleRangeService.niceTo(-15)).toEqual(-10); + expect(ScaleRangeService.niceTo(-1.5)).toEqual(-1); + expect(ScaleRangeService.niceTo(-0.15)).toEqual(-0.1); + expect(ScaleRangeService.niceTo(-0.015)).toEqual(-0.01); + expect(ScaleRangeService.niceTo(0)).toEqual(0); + })); }); }); }); \ No newline at end of file