├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build ├── bundle.js ├── d3-iconarray.js ├── d3-iconarray.min.js └── d3-iconarray.zip ├── images ├── examples.png ├── layout1.png ├── layout2.png ├── layout3.png ├── layoutheight.png ├── layoutwidth.png └── scaleexample.png ├── index.html ├── index.js ├── package.json ├── src ├── icon-array-layout.js └── icon-array-scale.js └── test └── iconarray-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | npm-debug.log 4 | 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | images 2 | index.html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016, Tom G Pearson 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of contributors may be used to 15 | endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # d3-iconarray 2 | 3 | ![examples](images/examples.png) 4 | 5 | A D3 plugin targeting V4 helping you to draw an array of icons. 6 | 7 | Why? 8 | 9 | There are two parts to this plugin. First a layout which will assign x,y coordinates to elements of an array given some parameters. Second a scale which will put regular breaks in the array of icons to aid legibility. 10 | 11 | ## Installing 12 | 13 | If you use NPM, `npm install d3-iconarray`. Otherwise, download the [latest release](https://github.com/tomgp/d3-iconarray/releases/latest). 14 | 15 | ## Examples 16 | 17 | * A minimal example using both layout and scale features 18 | * Using the plugin to make a hemicycle for election results 19 | * Recreating a GBS risk graphic from Scientific American 20 | * Setting the scale's gapSize and gapInterval properties 21 | * US Senate battlegrounds 22 | * when to use the widthFirst switch (UK election results) 23 | * Modal transport share with emojis 24 | 25 | ## API Reference 26 | 27 | ### Layout 28 | 29 | # d3_iconarray.layout() 30 | 31 | Construct a new icon array layout function. 32 | 33 | # layout([data array]) 34 | 35 | The function created by the above. When given an array of data will return an array containing grid positions as well as the original data. Unless a height or width restriction has been specified the layout will try to make the grid as square as possible. eg. a 100 element data array will result in a 10x10 grid. 36 | 37 | example 38 | 39 | ``` 40 | var layout = d3_iconarray.layout(); 41 | 42 | var grid = layout([1,2,3,4]); 43 | 44 | /* 45 | 'grid' is 46 | 47 | [ 48 | {"data":1,"position":{"x":0,"y":0}}, 49 | {"data":2,"position":{"x":1,"y":0}}, 50 | {"data":3,"position":{"x":0,"y":1}}, 51 | {"data":4,"position":{"x":1,"y":1}} 52 | ] 53 | 54 | /* 55 | 56 | ``` 57 | You can use the resulting grid to plot icons, the data points will be arranged like this 58 | 59 | ![layout 1](images/layout1.png) 60 | 61 | # layout.widthFirst([boolean]) 62 | 63 | This function sets the order in which points are arranged in the grid. if widthFirst is set to true rows will be filled before starting the next, if it's false columns in the layout will be filled first. if no argument is provided it returns the current value. 64 | 65 | example 66 | ``` 67 | var layout = d3_iconarray.layout() 68 | .widthFirst(true); 69 | 70 | var grid = layout([1,2,3,4]); 71 | ``` 72 | 73 | the resulting in the resulting grid the icons will be arranged like this 74 | 75 | ![layout 2](images/layout2.png) 76 | 77 | with ` .widthFirst(false)` they'll be arranged like this 78 | 79 | ![layout 3](images/layout3.png) 80 | 81 | when to use the widthFirst switch (UK election results) 82 | 83 | # layout.width([integer]) 84 | 85 | the width function defines the maximum number of elements the grid will have in a given row. if no argument is provided it returns the current value. 86 | 87 | example 88 | 89 | ``` 90 | var layout = d3_iconarray.layout().width(3); 91 | var grid = layout([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); 92 | ``` 93 | results in a grid like 94 | 95 | ![layout width restriction](images/layoutwidth.png) 96 | 97 | # layout.height([integer]) 98 | 99 | the height function sets the maximum number of elements the grid will have in a given row. if no argument is provided it returns the current value. 100 | 101 | example 102 | ``` 103 | var layout = d3_iconarray.layout().height(3); 104 | var grid = layout([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); 105 | ``` 106 | 107 | results in a grid like 108 | 109 | ![layout height restriction](images/layoutheight.png) 110 | 111 | # layout.position([integer]) 112 | 113 | given the a number representing an array element this function will tell you it's {x, y} location in the grid. 114 | This function needs some dimension of the grid (height or width) to have been set explicitly (by height or width) or implicitly by passing a data array to the layout function 115 | 116 | example 117 | 118 | ``` 119 | var layout = d3_iconarray.layout(); 120 | var grid = layout([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); 121 | 122 | var p = layout.position(7); 123 | // p is {x: 3, y: 1} 124 | 125 | ``` 126 | 127 | # layout.maxDimension([integer]) 128 | 129 | A bit like position but given a number will return the maximum extent of the lyout if it were to have that many elements. THis is useful for setting the domain of scales. 130 | 131 | 132 | ### Scale 133 | 134 | You can use any kind of scale to draw your grid to the screen. The scale provided by the plugin can let you add breaks to your icon array to improve legibility e.g. here's a 2500 element array grouped in to blocks of 100. 135 | 136 | ![blocks of 100](images/scaleexample.png) 137 | 138 | # d3_iconarray.scale() 139 | 140 | Creates a scale function just like good ol' d3.scaleLinear() etc. 141 | 142 | # scale(x) 143 | 144 | Given a value x in the input domain, returns the corresponding value in the output range. 145 | 146 | # scale.domain([numbers]) 147 | 148 | Set the input domain, an array of 2 numbers. 149 | 150 | If no arguments are provided this returns the current value. 151 | 152 | # scale.range([numbers]) 153 | 154 | Set the output range, an array of 2 numbers. 155 | 156 | If no arguments are provided this returns the current value. 157 | 158 | # scale.gapInterval(x) 159 | 160 | This function accepts a number which sets at what interval a gaps appear in the output range. i.e. if _x_ is 10 there will be an extra gap after every ten items in the output range. 161 | 162 | If no arguments are provided this returns the current value. 163 | 164 | see this example 165 | 166 | # scale.gapSize(x) 167 | 168 | This sets how big the gaps in the output range will be relative to the normal spacing. So if the normal spacing between two whole numbers is 10px and the gap size is set to 1.5 the extra wide space will be 15px. 169 | 170 | If no arguments are provided this returns the current value. 171 | 172 | see this example 173 | 174 | ## Why? 175 | # 176 | >Twenty years ago, a psychological study compared for the first time rudimentary icon displays for communicating risk. Today, we have dozens of randomized experiments to support the use of icon arrays (sometimes referred to as “pictographs”) as an evidence-based standard in medical risk communication. 177 | 178 | [Why use Icon Arrays](http://www.iconarray.com/why) 179 | 180 | >Icon arrays are particularly effective, and are apparently less likely to increase patient anxiety than other graphical techniques. 181 | 182 | [Scientific American: Inadequate Data Visualization Leaves Patients Undereducated](http://blogs.scientificamerican.com/sa-visual/inadequate-data-visualization-leaves-patients-undereducated/) 183 | 184 | 185 | -------------------------------------------------------------------------------- /build/bundle.js: -------------------------------------------------------------------------------- 1 | var version = "0.0.2"; export * from "../index"; export {version}; -------------------------------------------------------------------------------- /build/d3-iconarray.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-scale')) : 3 | typeof define === 'function' && define.amd ? define(['exports', 'd3-scale'], factory) : 4 | (factory((global.d3_iconarray = global.d3_iconarray || {}),global.d3)); 5 | }(this, function (exports,d3) { 'use strict'; 6 | 7 | function iconArrayLayout() { 8 | var width = undefined; 9 | var height = undefined; 10 | var widthFirst = true; 11 | var maxDimension = undefined; 12 | 13 | function layout(data){ 14 | //work our missing height, width stuff 15 | 16 | setDimensions(data.length); 17 | 18 | return data.map(function(d,i){ 19 | return { 20 | data:d, 21 | position:position(i) 22 | }; 23 | }); 24 | } 25 | 26 | function position(i){ 27 | if(isNaN(width) || isNaN(height)){ 28 | console.log('Warning: width/height undefined') 29 | return 0; 30 | } 31 | if(widthFirst){ 32 | return { 33 | x: i % width, 34 | y: Math.floor( i/width ) 35 | }; 36 | }else{ 37 | return { 38 | x: Math.floor( i/height ), 39 | y: i % height 40 | }; 41 | } 42 | } 43 | 44 | function setDimensions(l){ 45 | //neither width or height is defined 46 | if(isNaN(width) && isNaN(height)){ 47 | console.log('no width or height'); 48 | if(widthFirst){ 49 | width = Math.ceil( Math.sqrt(l) ); 50 | height = Math.ceil( l / width ); 51 | }else{ 52 | height = Math.ceil( Math.sqrt(l) ); 53 | width = Math.ceil( l / height ); 54 | } 55 | }else if(isNaN(width)){ //width undefined 56 | width = Math.ceil( l / height ); 57 | }else if(isNaN(height)){ //height undefined 58 | height = Math.ceil( l / width ); 59 | } 60 | } 61 | 62 | layout.maxDimension = function(x){ 63 | var itemPosition = position(x); 64 | if(widthFirst){ 65 | var x = Math.max(itemPosition.x, width); 66 | return Math.max(x, itemPosition.y); 67 | } 68 | var y = Math.max(itemPosition.y, height); 69 | return Math.max(y, itemPosition.x); 70 | 71 | } 72 | 73 | layout.position = function(x){ 74 | return position(x); 75 | } 76 | 77 | layout.width = function(x){ 78 | if(x === undefined) return width; 79 | width = x; 80 | return layout; 81 | }; 82 | 83 | layout.height = function(x){ 84 | if(x === undefined) return height; 85 | height = x; 86 | return layout; 87 | }; 88 | 89 | layout.widthFirst = function(b){ 90 | if(b === undefined) return widthFirst; 91 | widthFirst = b; 92 | return layout; 93 | }; 94 | 95 | return layout; 96 | }; 97 | 98 | function iconArrayScale(){ 99 | 100 | var domain = [0,100]; 101 | var range = [0,100]; 102 | var gapInterval = 10; 103 | var gapSize = 0; //default no change 104 | var notionalScale = d3.scaleLinear() 105 | .domain(domain) 106 | .range(range); 107 | 108 | function scale(domainValue){ 109 | var rangeValue = 20; 110 | var adjustedDomainValue = domainValue + Math.floor(domainValue/gapInterval)*gapSize; 111 | //console.log(notionalScale.domain()); 112 | return rangeValue = notionalScale(adjustedDomainValue); 113 | } 114 | 115 | function rescale(){ 116 | //calculate an adjusted domain 117 | var domainLength = (domain[1] - domain[0]) * gapSize; 118 | var gaps = Math.ceil( domainLength/ gapInterval ); 119 | var adjustedDomain = [ domain[0], domain[1] + gaps ]; 120 | 121 | //calculate an adjusted range 122 | 123 | notionalScale.domain(adjustedDomain) 124 | .range(range); 125 | } 126 | 127 | scale.gapInterval = function(x){ 128 | if(!x) return gapInterval; 129 | gapInterval = x; 130 | rescale(); 131 | return scale; 132 | }; 133 | 134 | scale.gapSize = function(x){ 135 | if(isNaN(x)) return gapSize; 136 | gapSize = x; 137 | rescale(); 138 | return scale; 139 | } 140 | 141 | scale.domain = function(array){ 142 | if(!array) return domain; 143 | domain = array; 144 | rescale(); 145 | return scale; 146 | }; 147 | 148 | scale.range = function(array){ 149 | if(!array) return range; 150 | range = array; 151 | rescale(); 152 | return scale; 153 | }; 154 | 155 | rescale(); 156 | return scale; 157 | } 158 | 159 | var version = "0.0.2"; 160 | 161 | exports.version = version; 162 | exports.layout = iconArrayLayout; 163 | exports.scale = iconArrayScale; 164 | 165 | })); -------------------------------------------------------------------------------- /build/d3-iconarray.min.js: -------------------------------------------------------------------------------- 1 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-scale")):"function"==typeof define&&define.amd?define(["exports","d3-scale"],t):t(n.d3_iconarray=n.d3_iconarray||{},n.d3)}(this,function(n,t){"use strict";function i(){function n(n){return i(n.length),n.map(function(n,i){return{data:n,position:t(i)}})}function t(n){return isNaN(r)||isNaN(e)?(console.log("Warning: width/height undefined"),0):a?{x:n%r,y:Math.floor(n/r)}:{x:Math.floor(n/e),y:n%e}}function i(n){isNaN(r)&&isNaN(e)?(console.log("no width or height"),a?(r=Math.ceil(Math.sqrt(n)),e=Math.ceil(n/r)):(e=Math.ceil(Math.sqrt(n)),r=Math.ceil(n/e))):isNaN(r)?r=Math.ceil(n/e):isNaN(e)&&(e=Math.ceil(n/r))}var r=void 0,e=void 0,a=!0;return n.maxDimension=function(n){var i=t(n);if(a){var n=Math.max(i.x,r);return Math.max(n,i.y)}var o=Math.max(i.y,e);return Math.max(o,i.x)},n.position=function(n){return t(n)},n.width=function(t){return void 0===t?r:(r=t,n)},n.height=function(t){return void 0===t?e:(e=t,n)},n.widthFirst=function(t){return void 0===t?a:(a=t,n)},n}function r(){function n(n){var t=20,i=n+Math.floor(n/a)*o;return t=u(i)}function i(){var n=(r[1]-r[0])*o,t=Math.ceil(n/a),i=[r[0],r[1]+t];u.domain(i).range(e)}var r=[0,100],e=[0,100],a=10,o=0,u=t.scaleLinear().domain(r).range(e);return n.gapInterval=function(t){return t?(a=t,i(),n):a},n.gapSize=function(t){return isNaN(t)?o:(o=t,i(),n)},n.domain=function(t){return t?(r=t,i(),n):r},n.range=function(t){return t?(e=t,i(),n):e},i(),n}var e="0.0.2";n.version=e,n.layout=i,n.scale=r}); -------------------------------------------------------------------------------- /build/d3-iconarray.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomgp/d3-iconarray/c12224923c46acf5591a3ef8713f41c747323b4e/build/d3-iconarray.zip -------------------------------------------------------------------------------- /images/examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomgp/d3-iconarray/c12224923c46acf5591a3ef8713f41c747323b4e/images/examples.png -------------------------------------------------------------------------------- /images/layout1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomgp/d3-iconarray/c12224923c46acf5591a3ef8713f41c747323b4e/images/layout1.png -------------------------------------------------------------------------------- /images/layout2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomgp/d3-iconarray/c12224923c46acf5591a3ef8713f41c747323b4e/images/layout2.png -------------------------------------------------------------------------------- /images/layout3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomgp/d3-iconarray/c12224923c46acf5591a3ef8713f41c747323b4e/images/layout3.png -------------------------------------------------------------------------------- /images/layoutheight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomgp/d3-iconarray/c12224923c46acf5591a3ef8713f41c747323b4e/images/layoutheight.png -------------------------------------------------------------------------------- /images/layoutwidth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomgp/d3-iconarray/c12224923c46acf5591a3ef8713f41c747323b4e/images/layoutwidth.png -------------------------------------------------------------------------------- /images/scaleexample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomgp/d3-iconarray/c12224923c46acf5591a3ef8713f41c747323b4e/images/scaleexample.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | D3 icon array 5 | 6 | " 7 | 8 | 9 | 10 | 11 |
12 |

D3 icon array

13 |

Tom Pearson Mar 2016

14 |

A D3 plugin helping you to draw an array of icons. Works fine with v4 or v5 (possibly later?)

15 |

Check out the readme file for usage examples, API docs, and more information on the rationale behind using icon arrays.

16 |
17 | 18 |
19 |
20 | 21 | 60 | 61 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export {default as layout} from "./src/icon-array-layout"; 2 | export {default as scale} from "./src/icon-array-scale"; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-iconarray", 3 | "version": "0.0.3", 4 | "description": "Layout a grid of icons", 5 | "keywords": [ 6 | "d3","visualization","iconarray","layout" 7 | ], 8 | "author": "Tom G Pearson", 9 | "license": "BSD-3-Clause", 10 | "main": "build/d3-iconarray.js", 11 | "jsnext:main": "index", 12 | "homepage": "https://www.github.com/tomgp/d3-iconarray", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://www.github.com/tomgp/d3-iconarray.git" 16 | }, 17 | "scripts": { 18 | "pretest": "mkdir -p build && node -e 'process.stdout.write(\"var version = \\\"\" + require(\"./package.json\").version + \"\\\"; export * from \\\"../index\\\"; export {version};\");' > build/bundle.js && rollup -f umd -n d3_iconarray -o build/d3-iconarray.js -- build/bundle.js", 19 | "test": "faucet `find test -name '*-test.js'`", 20 | "prepublish": "npm run test && uglifyjs build/d3-iconarray.js -c -m -o build/d3-iconarray.min.js && rm -f build/d3-iconarray.zip && zip -j build/d3-iconarray.zip -- LICENSE README.md build/d3-iconarray.js build/d3-iconarray.min.js" 21 | }, 22 | "devDependencies": { 23 | "faucet": "0.0", 24 | "rollup": "0.25", 25 | "tape": "4", 26 | "uglify-js": "2.6" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/icon-array-layout.js: -------------------------------------------------------------------------------- 1 | export default function() { 2 | var width = undefined; 3 | var height = undefined; 4 | var widthFirst = true; 5 | var maxDimension = undefined; 6 | 7 | function layout(data){ 8 | //work our missing height, width stuff 9 | 10 | setDimensions(data.length); 11 | 12 | return data.map(function(d,i){ 13 | return { 14 | data:d, 15 | position:position(i) 16 | }; 17 | }); 18 | } 19 | 20 | function position(i){ 21 | if(isNaN(width) || isNaN(height)){ 22 | console.log('Warning: width/height undefined') 23 | return 0; 24 | } 25 | if(widthFirst){ 26 | return { 27 | x: i % width, 28 | y: Math.floor( i/width ) 29 | }; 30 | }else{ 31 | return { 32 | x: Math.floor( i/height ), 33 | y: i % height 34 | }; 35 | } 36 | } 37 | 38 | function setDimensions(l){ 39 | //neither width or height is defined 40 | if(isNaN(width) && isNaN(height)){ 41 | console.log('no width or height'); 42 | if(widthFirst){ 43 | width = Math.ceil( Math.sqrt(l) ); 44 | height = Math.ceil( l / width ); 45 | }else{ 46 | height = Math.ceil( Math.sqrt(l) ); 47 | width = Math.ceil( l / height ); 48 | } 49 | }else if(isNaN(width)){ //width undefined 50 | width = Math.ceil( l / height ); 51 | }else if(isNaN(height)){ //height undefined 52 | height = Math.ceil( l / width ); 53 | } 54 | } 55 | 56 | layout.maxDimension = function(x){ 57 | var itemPosition = position(x); 58 | if(widthFirst){ 59 | var x = Math.max(itemPosition.x, width); 60 | return Math.max(x, itemPosition.y); 61 | } 62 | var y = Math.max(itemPosition.y, height); 63 | return Math.max(y, itemPosition.x); 64 | 65 | } 66 | 67 | layout.position = function(x){ 68 | return position(x); 69 | } 70 | 71 | layout.width = function(x){ 72 | if(x === undefined) return width; 73 | width = x; 74 | return layout; 75 | }; 76 | 77 | layout.height = function(x){ 78 | if(x === undefined) return height; 79 | height = x; 80 | return layout; 81 | }; 82 | 83 | layout.widthFirst = function(b){ 84 | if(b === undefined) return widthFirst; 85 | widthFirst = b; 86 | return layout; 87 | }; 88 | 89 | return layout; 90 | }; 91 | 92 | 93 | 94 | function position(item, width, height, widthFirst){ 95 | return i; 96 | } -------------------------------------------------------------------------------- /src/icon-array-scale.js: -------------------------------------------------------------------------------- 1 | import * as d3 from 'd3-scale'; 2 | 3 | export default function(){ 4 | 5 | var domain = [0,100]; 6 | var range = [0,100]; 7 | var gapInterval = 10; 8 | var gapSize = 0; //default no change 9 | var notionalScale = d3.scaleLinear() 10 | .domain(domain) 11 | .range(range); 12 | 13 | function scale(domainValue){ 14 | var rangeValue = 20; 15 | var adjustedDomainValue = domainValue + Math.floor(domainValue/gapInterval)*gapSize; 16 | //console.log(notionalScale.domain()); 17 | return rangeValue = notionalScale(adjustedDomainValue); 18 | } 19 | 20 | function rescale(){ 21 | //calculate an adjusted domain 22 | var domainLength = (domain[1] - domain[0]) * gapSize; 23 | var gaps = Math.ceil( domainLength/ gapInterval ); 24 | var adjustedDomain = [ domain[0], domain[1] + gaps ]; 25 | 26 | //calculate an adjusted range 27 | 28 | notionalScale.domain(adjustedDomain) 29 | .range(range); 30 | } 31 | 32 | scale.gapInterval = function(x){ 33 | if(!x) return gapInterval; 34 | gapInterval = x; 35 | rescale(); 36 | return scale; 37 | }; 38 | 39 | scale.gapSize = function(x){ 40 | if(isNaN(x)) return gapSize; 41 | gapSize = x; 42 | rescale(); 43 | return scale; 44 | } 45 | 46 | scale.domain = function(array){ 47 | if(!array) return domain; 48 | domain = array; 49 | rescale(); 50 | return scale; 51 | }; 52 | 53 | scale.range = function(array){ 54 | if(!array) return range; 55 | range = array; 56 | rescale(); 57 | return scale; 58 | }; 59 | 60 | rescale(); 61 | return scale; 62 | } -------------------------------------------------------------------------------- /test/iconarray-test.js: -------------------------------------------------------------------------------- 1 | var tape = require("tape"), 2 | iconArray = require("../"); 3 | 4 | tape("check empty layout", function(test) { 5 | var myArray = iconArray.layout(); 6 | test.equal( myArray([]).length, 0 ); 7 | test.end(); 8 | }); 9 | //TODO check default layout i.e. no width height specified 10 | 11 | tape("check 10x10 layout", function(test) { // a nice neat 10x10 12 | var myArray = iconArray.layout() 13 | .width(10) 14 | .height(10); 15 | 16 | var layout = myArray(getTestData(100)); 17 | 18 | test.equal( layout.length, 100 ); 19 | test.equal( layout[0].position.x, 0); 20 | test.equal( layout[0].position.y, 0); 21 | test.equal( layout[10].position.x, 0); 22 | test.equal( layout[10].position.y, 1); 23 | 24 | test.equal( myArray.position(25).x, 5); 25 | test.equal( myArray.position(25).y, 2); 26 | test.equal( myArray.width(), 10); 27 | test.equal( myArray.height(), 10); 28 | test.end(); 29 | }); 30 | 31 | tape("check 10x? layout", function(test){ 32 | var myArray = iconArray.layout() 33 | .width(10); 34 | 35 | var layout = myArray(getTestData(900)); 36 | test.equal( layout.length, 900 ); 37 | test.equal( myArray.position(320).x, 0); 38 | test.equal( myArray.position(320).y, 32); 39 | test.equal( myArray.width(), 10); 40 | test.equal( myArray.height(), 90); 41 | test.end(); 42 | }); 43 | 44 | 45 | tape("check empty scale", function(test){ 46 | var myScale = iconArray.scale(); 47 | test.equal( myScale(20), 20 ); 48 | test.end(); 49 | }); 50 | 51 | //TODO more scale tests 52 | 53 | function getTestData(n){ 54 | var a = []; 55 | for(var i=0; i