├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── doc └── index.md ├── examples ├── css │ └── style.css ├── index.html ├── js │ ├── contour-df-styled.js │ ├── contour-df.js │ ├── plot.js │ └── scatter.js ├── simple.html ├── simple_sushi.html └── test.html └── src ├── soba.css ├── soba.js ├── soba_modal.css └── soba_modal.js /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject 2 | .project 3 | .settings 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sushi"] 2 | path = sushi 3 | url = https://github.com/mil-tokyo/sushi.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Machine Intelligence Laboratory (The University of Tokyo) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Soba Javascript Library 2 | 3 | A visualization toolkit coordinating with Sushi and Tempura 4 | 5 | Soba is a 2D plotting library integrated with Sushi (https://github.com/mil-tokyo/sushi) written in javascript. This library is easy to use, can work in web browsers and coordinate with Sushi and Tempura(https://github.com/mil-tokyo/tempura). 6 | 7 | Please visit Tempura demo page(http://mil-tokyo.github.io/tempura/demo/index.html) to see figures drawn by this library. 8 | 9 | Related papers are available ( http://mil-tokyo.github.io/miljs.html ). 10 | 11 | #Introduction 12 | 13 | Soba Javascript Library is a javascript 2D plotting library which is designed to draw high quality figures for scientific computation easily as in matplotlib (http://matplotlib.org/). 14 | Soba Javascript Library is written in javascript so that it can work on web browsers in any environment and can coordinate with Sushi (https://github.com/mil-tokyo/sushi) and Tempura (https://github.com/mil-tokyo/tempura). 15 | 16 | #How to use 17 | ## Clone the repo or download the files 18 | On a console, type 19 | ``` 20 | git clone git@github.com:mil-tokyo/soba.git 21 | ``` 22 | or simply download the file from 23 | https://github.com/mil-tokyo/soba/archive/master.zip 24 | 25 | src/soba.js and src/soba.css are core files. 26 | 27 | ## Include files in your HTML 28 | Soba works with HTML in web browsers and depends on d3.js (http://d3js.org/). 29 | Please include d3.js and Soba codes in your HTML file as following: 30 | 31 | ```HTML 32 | 33 | 34 | 35 | ``` 36 | 37 | ## Create an instance of Soba class to draw figure in a DOM object 38 | Soba draws a figure as a SVG object in a DOM object. 39 | For example, Soba creates and initializes a SVG object in a DOM object whose id is "figure1" by the following code: 40 | 41 | ```javascript 42 | var plt = new Soba('#figure1'); 43 | ``` 44 | 45 | Soba uses d3.js to handle SVG objects. 46 | 47 | ## Draw graph on the figure 48 | Now, let's draw graphs! 49 | For example, plot() plots continuous points and connects those points by line if a specific option is given like this: 50 | ```javascript 51 | plt.plot(x,y,options); 52 | plt.show(); 53 | ``` 54 | Here, variables x and y represent the locations of the points and the styles of the points and the line can be controlled by options as a string. 55 | 56 | ## Full simple example 57 | Here, the whole code is shown to plot one sine curve. 58 | 59 | http://mil-tokyo.github.io/soba/examples/simple.html 60 | ```HTML 61 | 62 | 63 | 64 | Simple Soba sample 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
73 | 87 | 88 | 89 | ``` 90 | 91 | # Integration with Sushi 92 | Soba can work with Sushi Javascript Library (https://github.com/mil-tokyo/sushi) which offers fast matrix calculation in javascript. 93 | Sushi's matrix class *Sushi.Matrix* can be passed to Soba's methods. 94 | For example, the sample code above can be written as following by using *Sushi.Matrix*: 95 | 96 | http://mil-tokyo.github.io/soba/examples/simple_sushi.html 97 | 98 | ```HTML 99 | 100 | 101 | 102 | Simple Soba sample 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
112 | 129 | 130 | 131 | ``` 132 | The plotting code is unchanged while the data passed to plot() method are changed to instances of *Sushi.Matrix* (and sushi.js is included). 133 | 134 | # Learn more 135 | Please see the documents [here](https://github.com/mil-tokyo/soba/blob/master/doc/index.md). You can get more detailed information such as method references. 136 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | # Methods 2 | 3 | ## Constructor 4 | ### Syntax 5 | ```javascript 6 | var plt = new Soba(selector); 7 | ``` 8 | * *selector* : CSS selector to specify a DOM object which contains the figure drawn by the Soba instance 9 | 10 | ### Description 11 | By constructor, Soba class creates and initializes a SVG object to draw figure in a DOM object which is specified by *selector*. 12 | 13 | ### Sample 14 | ```HTML 15 |
16 | 19 | ``` 20 | 21 | ## plot() 22 | ### Syntax 23 | ```javascript 24 | plt.plot(x, y, options); 25 | ``` 26 | * *x* : 1-dimentional Array or n-by-1 shaped Sushi.Matrix object which represents x-coordinates of data points 27 | * *y* : 1-dimentional Array or n-by-1 shaped Sushi.Matrix object which represents y-coordinates of data points 28 | * *options (default:"b-")* : String to specify styles of points and line. Supported styles are following. Combination of these characters represents the style. 29 | 30 | | Character | Line Style | 31 | | --------- |--------------------:| 32 | | - | solid line style | 33 | | -- | dashed line style | 34 | | -. | dash-dot line style | 35 | | : | dotted line style | 36 | | o | circle marker | 37 | 38 | | Character | Color | 39 | | --------- |--------:| 40 | | b | blue | 41 | | g | green | 42 | | r | red | 43 | | c | cyan | 44 | | m | magenta | 45 | | y | yellow | 46 | | k | black | 47 | | w | white | 48 | 49 | ### Description 50 | This method plots continuous points and connects those points by line if a specific option is given. The styles of points and line are controlled by *options* argument. 51 | 52 | ### Sample 53 | ```javascript 54 | var x=..., y=...; 55 | plt.plot(x,y,'k--'); 56 | ``` 57 | Please see also http://mil-tokyo.github.io/soba/examples/index.html#plot 58 | 59 | ## scatter() 60 | ### Syntax 61 | ```javascript 62 | plt.scatter(x, y, colors); 63 | ``` 64 | * *x* : 1-dimentional Array or n-by-1 shaped Sushi.Matrix object which represents x-coordinates of data points 65 | * *y* : 1-dimentional Array or n-by-1 shaped Sushi.Matrix object which represents y-coordinates of data points 66 | * *colors* : n-by-1 shaped Sushi.Matrix object which represents colors of data points. The elements of this matrix are integer (1,2,3,...). 67 | 68 | ### Description 69 | This method makes a scatter plot of x vs y. Colors of those are controlled by *colors* argument. 70 | 71 | ### Sample 72 | ```javascript 73 | var x=new Sushi.Matrix(...), y=new Sushi.Matrix(...), colors=new Sushi.Matrix(...); 74 | x=...; y=...; colors=...; 75 | plt.scatter(x,y,colors); 76 | ``` 77 | Please see also http://mil-tokyo.github.io/soba/examples/index.html#scatter 78 | 79 | ## contourDesicionFunction() 80 | ### Syntax 81 | ```javascript 82 | plt.contourDesicionFunction(x_min, x_max, y_min, y_max, callback); 83 | plt.contourDesicionFunction(x_min, x_max, y_min, y_max, options, callback); 84 | ``` 85 | * *x_min* : x-coordinate of the left edge of the plotting area 86 | * *x_max* : x-coordinate of the right edge of the plotting area 87 | * *y_min* : y-coordinate of the bottom edge of the plotting area 88 | * *y_max* : y-coordinate of the top edge of the plotting area 89 | * *options* : Object specifying some options like {option_name1: value1, option_name2: value2, ...}. Supported options are following: 90 | 91 | | Option name | Description | 92 | | ----------- |--------| 93 | | levels | Array containing the contour levels. If not provided, the contour levels are determined automatically.| 94 | | colors | Array containing the colors of contour lines. Colors are specified by the aliases described above(plot()). If not provided, colors are determined automatically. If the length of this array is shorter than that of levels, specified colors are repeated. | 95 | | linestyles | Array containing the line styles of contour lines. Line styles are specified by strings. 'solid' and 'dashed' are supported. If not provided, all lines are solid style. If the length of this array is shorter than that of levels, specified line styles are repeated.| 96 | * *callback* : Callback function that takes 2 arguments *x* and *y* and returns function value to draw contour. 97 | 98 | ### Description 99 | This method plots contours of a function specified by *callback* function. The plotting area is specified by *x_min*, *x_max*, *y_min* and *y_max* and *callback* is called over lattice points in the plotting area to assign function values to each point. 100 | 101 | ### Sample 102 | ```javascript 103 | plt.contourDesicionFunction(-3, 3, -3, 3, {levels: [0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.], colors: ['m','b'], linestyles: ['dashed', 'solid']}, function(x,y){ 104 | var z = Math.exp(-(x*x+y*y)/2)/(2*Math.PI); // Gaussian distribution 105 | return z; 106 | }); 107 | ``` 108 | Please see also http://mil-tokyo.github.io/soba/examples/index.html#contour-df 109 | 110 | ## xlim() 111 | ### Syntax 112 | ```javascript 113 | plt.xlim([x_min, x_max]); 114 | ``` 115 | * *x_min (default: null)* : A number specifying the minimum of *x* axis. If not specified, it is determined automatically. 116 | * *x_max (default: null)* : A number specifying the maximum of *x* axis. If not specified, it is determined automatically. 117 | 118 | ### Description 119 | Set the limits for *x*. 120 | 121 | ### Sample 122 | ```javascript 123 | plt.xlim([0, null]); 124 | ``` 125 | 126 | ## ylim() 127 | ### Syntax 128 | ```javascript 129 | plt.ylim([y_min, y_max]); 130 | ``` 131 | (Arguments are similar to xlim()) 132 | 133 | ### Description 134 | Set the limits for *y*. 135 | 136 | ### Sample 137 | ```javascript 138 | plt.ylim([0, null]); 139 | ``` 140 | 141 | ## legend() 142 | ### Syntax 143 | ```javascript 144 | plt.legend(labels, location); 145 | ``` 146 | * *labels* : Array containing titles of figure elements 147 | * *location (default:"upper right")* : String to specify the legend location. 'right', 'left, 'upper' and 'bottom' are supported. These can be combined such as 'bottom left'. 148 | 149 | ### Description 150 | Place a legend in the figure. 151 | 152 | ### Sample 153 | ```javascript 154 | plt.legend(['label1', 'label2', ...], "bottom left"); 155 | ``` 156 | 157 | ## xlabel() 158 | ### Syntax 159 | ```javascript 160 | plt.xlabel(label, options); 161 | ``` 162 | * *label* : String representing *x* axis label 163 | * *options* : Object specifying some options like {option_name1: value1, option_name2: value2, ...}. Supported options are following: 164 | 165 | | Option name | Description | 166 | | ----------- |--------| 167 | | fontsize | Integer specifying the font size| 168 | 169 | ### Description 170 | Set the x axis label. 171 | 172 | ### Sample 173 | ```javascript 174 | plt.xlabel('x'); 175 | ``` 176 | 177 | ## ylabel() 178 | ### Syntax 179 | ```javascript 180 | plt.ylabel(label, options); 181 | ``` 182 | (Arguments are the same as xlabel()) 183 | 184 | ### Description 185 | Set the y axis label. 186 | 187 | ### Sample 188 | ```javascript 189 | plt.ylabel('y'); 190 | ``` 191 | 192 | ## colorbar() 193 | ### Syntax 194 | ```javascript 195 | plt.colorbar(); 196 | ``` 197 | 198 | ### Description 199 | Add a colorbar to a plot corresponding to a contour. 200 | 201 | ### Sample 202 | ```javascript 203 | plt.colorbar(); 204 | ``` 205 | 206 | ## show() 207 | ### Syntax 208 | ```javascript 209 | plt.show(); 210 | ``` 211 | 212 | ### Description 213 | Display a figure. Nothing is drawn until this method is called. This method MUST be called to draw a figure. 214 | 215 | ### Sample 216 | ```javascript 217 | plt.show(); 218 | ``` 219 | 220 | ## clf() 221 | ### Syntax 222 | ```javascript 223 | plt.clf(); 224 | ``` 225 | 226 | ### Description 227 | Clear all figures before show() is called. 228 | 229 | ### Sample 230 | ```javascript 231 | plt.clf(); 232 | ``` 233 | -------------------------------------------------------------------------------- /examples/css/style.css: -------------------------------------------------------------------------------- 1 | .content-wrapper{ 2 | clear: both; 3 | } 4 | .content-wrapper h3 { 5 | padding: 5px; 6 | background-color: #eee; 7 | border: 1px solid #ddd; 8 | } 9 | .figure{ 10 | border: solid 1px #000; 11 | width: 640px; 12 | height: 480px; 13 | float: left; 14 | } 15 | .source{ 16 | float: left; 17 | } -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple Soba sample 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

plot()

15 |
16 |
17 | 18 |
19 | 20 |
21 |
22 |

scatter()

23 |
> 24 |
25 | 26 |
27 |
28 |
29 |

contourDecisionFunction()

30 |
31 |
32 | 33 |
34 |
35 |
36 |

contourDecisionFunction() with line styles

37 |
38 |
39 | 40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/js/contour-df-styled.js: -------------------------------------------------------------------------------- 1 | // Gaussian distribution 2 | var plt = new Soba('#contour-df-styled'); 3 | plt.contourDesicionFunction(-3, 3, -3, 3, {levels: [0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.], colors: ['m','b'], linestyles: ['dashed', 'solid']}, function(x,y){ 4 | var z = Math.exp(-(x*x+y*y)/2)/(2*Math.PI); 5 | return z; 6 | }); 7 | plt.legend(['gaussian']); 8 | plt.xlabel('x'); 9 | plt.ylabel('y'); 10 | plt.show(); -------------------------------------------------------------------------------- /examples/js/contour-df.js: -------------------------------------------------------------------------------- 1 | // Gaussian distribution 2 | var plt = new Soba('#contour-df'); 3 | plt.contourDesicionFunction(-3, 3, -3, 3, function(x,y){ 4 | var z = Math.exp(-(x*x+y*y)/2)/(2*Math.PI); 5 | return z; 6 | }); 7 | plt.colorbar(); 8 | plt.xlabel('x'); 9 | plt.ylabel('y'); 10 | plt.show(); -------------------------------------------------------------------------------- /examples/js/plot.js: -------------------------------------------------------------------------------- 1 | // Sine data 2 | var x_min=-3, x_max=3, nbins = 60; 3 | var x = new Array(nbins), y = new Array(nbins); 4 | for (var i=0 ; i 2 | 3 | 4 | Simple Soba sample 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/simple_sushi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple Soba sample 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 31 | 32 | -------------------------------------------------------------------------------- /examples/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Soba - A visualization tool for sushi.js 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /src/soba.css: -------------------------------------------------------------------------------- 1 | .axis path, 2 | .axis line { 3 | fill: none; 4 | stroke: black; 5 | shape-rendering: crispEdges; 6 | } 7 | 8 | .axis text { 9 | font-family: sans-serif; 10 | font-size: 11px; 11 | } 12 | 13 | .colorbar-axis path, 14 | .colorbar-axis line { 15 | fill: none; 16 | stroke: black; 17 | shape-rendering: crispEdges; 18 | } 19 | 20 | .colorbar-axis text { 21 | font-family: sans-serif; 22 | font-size: 11px; 23 | } -------------------------------------------------------------------------------- /src/soba.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2014 Machine Intelligence Laboratory (The University of Tokyo) 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | var Soba = {}; 24 | 25 | (function($M){ 26 | Soba = function(selector) { 27 | this.selector = selector; 28 | this.padding = { 29 | left: 30, 30 | right: 20, 31 | top: 20, 32 | bottom: 20, 33 | }; 34 | this.margin = { 35 | left: 0, 36 | right: 0, 37 | top: 0, 38 | bottom: 0, 39 | }; 40 | var parent = d3.select(selector); 41 | this.w = Soba.getStyle(parent[0][0], 'width'); 42 | this.h = Soba.getStyle(parent[0][0], 'height'); 43 | this.w = this.w.replace('px', ''); 44 | this.h = this.h.replace('px', ''); 45 | var svg = parent 46 | .append("svg") 47 | .attr('width', this.w) 48 | .attr('height', this.h) 49 | ; 50 | this.svg = svg; 51 | this.clf(); 52 | }; 53 | 54 | Soba.prototype = { 55 | clf: function() { 56 | this.elements = []; 57 | this.surroundings = []; 58 | this.svg.selectAll('*').remove(); 59 | }, 60 | 61 | plot: function(x, y, option) { 62 | var obj = new Soba.Plot( 63 | ($M && x instanceof $M) ? x.clone() : x, 64 | ($M && y instanceof $M) ? y.clone() : y, 65 | option 66 | ); 67 | this.elements.push(obj); 68 | }, 69 | 70 | scatter: function(x, y, color) { 71 | var obj = new Soba.Scatter( 72 | ($M && x instanceof $M) ? x.clone() : x, 73 | ($M && y instanceof $M) ? y.clone() : y, 74 | ($M && color instanceof $M) ? color.clone() : color 75 | ); 76 | this.elements.push(obj); 77 | }, 78 | 79 | contourDesicionFunction: function(x_min, x_max, y_min, y_max, func, func_){ 80 | var decision_func; 81 | if (typeof func === 'function') { 82 | decision_func = func; 83 | var args = {}; 84 | } else { 85 | decision_func = func_; 86 | var args = func; 87 | } 88 | 89 | var obj = new Soba.ContourDesicionFunction(x_min, x_max, y_min, y_max, decision_func, args); 90 | this.elements.push(obj); 91 | }, 92 | 93 | xlim: function(x_range) { 94 | if (!(0 in x_range) || !(1 in x_range)) { 95 | throw new TypeError('x_range must be an array and contain 2 elements'); 96 | } 97 | this.x_range = x_range; 98 | }, 99 | 100 | ylim: function(y_range) { 101 | if (!(0 in y_range) || !(1 in y_range)) { 102 | throw new TypeError('y_range must be an array and contain 2 elements'); 103 | } 104 | this.y_range = y_range; 105 | }, 106 | 107 | xlabel: function(title, options) { 108 | var obj = new Soba.Label(title, options, 0); 109 | var g = this._reserveSurrounding('bottom', 40); 110 | this.surroundings.push([obj, g]); 111 | }, 112 | 113 | ylabel: function(title, options) { 114 | var obj = new Soba.Label(title, options, 270); 115 | var g = this._reserveSurrounding('left', 40); 116 | this.surroundings.push([obj, g]); 117 | }, 118 | 119 | legend: function(titles, location) { 120 | var obj = new Soba.Legend(this.elements, titles, location ? location : null, this.padding, this.margin); 121 | this.elements.push(obj); 122 | }, 123 | 124 | colorbar: function() { 125 | this.elements.forEach(function(d){ 126 | if (d instanceof Soba.ContourDesicionFunction) { 127 | var obj = new Soba.Colorbar(d); 128 | var g = this._reserveSurrounding('right', 50); 129 | this.surroundings.push([obj, g]); 130 | } 131 | }, this); 132 | }, 133 | 134 | _reserveSurrounding: function(location, width) { 135 | var g = this.svg.append('g'); 136 | switch (location) { 137 | case 'top': 138 | break; 139 | 140 | case 'bottom': 141 | var x = this.margin.left; 142 | var y = this.h - this.margin.bottom - width; 143 | g 144 | .attr('transform', 'translate('+x+','+y+')') 145 | .attr('width', this.w - this.margin.left - this.margin.right) 146 | .attr('height', width) 147 | ; 148 | this.margin.bottom += width; 149 | break; 150 | 151 | case 'right': 152 | var x = this.w - this.margin.right - width; 153 | var y = this.margin.top; 154 | g 155 | .attr('transform', 'translate('+x+','+y+')') 156 | .attr('width', width) 157 | .attr('height', this.h - this.margin.top - this.margin.bottom) 158 | ; 159 | this.margin.right += width; 160 | break; 161 | 162 | case 'left': 163 | var x = this.margin.left; 164 | var y = this.margin.top; 165 | g 166 | .attr('transform', 'translate('+x+','+y+')') 167 | .attr('width', width) 168 | .attr('height', this.h - this.margin.top - this.margin.bottom) 169 | ; 170 | this.margin.left += width; 171 | break; 172 | } 173 | 174 | return g; 175 | }, 176 | 177 | show: function() { 178 | // Determine the ranges 179 | var x_range_init = this.x_range, y_range_init = this.y_range; 180 | var x_range = null, y_range = null; 181 | this.elements.forEach(function(d) { 182 | if (d.x_range) { 183 | if (x_range) { 184 | if (!x_range_init || x_range_init[0]===null) x_range[0] = Math.min(x_range[0], d.x_range()[0]); 185 | if (!x_range_init || x_range_init[1]===null) x_range[1] = Math.max(x_range[1], d.x_range()[1]); 186 | } else { 187 | if (x_range_init) { 188 | x_range = [x_range_init[0]!==null ? x_range_init[0] : d.x_range()[0], x_range_init[1]!==null ? x_range_init[1] : d.x_range()[1]]; 189 | } else { 190 | x_range = d.x_range(); 191 | } 192 | } 193 | } 194 | if (d.y_range) { 195 | if (y_range) { 196 | if (!y_range_init || y_range_init[0]===null) y_range[0] = Math.min(y_range[0], d.y_range()[0]); 197 | if (!y_range_init || y_range_init[1]===null) y_range[1] = Math.max(y_range[1], d.y_range()[1]); 198 | } else { 199 | if (y_range_init) { 200 | y_range = [y_range_init[0]!==null ? y_range_init[0] : d.y_range()[0], y_range_init[1]!==null ? y_range_init[1] : d.y_range()[1]]; 201 | } else { 202 | y_range = d.y_range(); 203 | } 204 | } 205 | } 206 | }); 207 | y_range[1] = [y_range[0], y_range[0] = y_range[1]][0]; // Swap 208 | 209 | var xScale = d3.scale.linear() 210 | .domain(x_range) 211 | .range([this.padding.left + this.margin.left, this.w - this.padding.right - this.margin.right]); 212 | 213 | var yScale = d3.scale.linear() 214 | .domain(y_range) 215 | .range([this.padding.top + this.margin.top, this.h-this.padding.bottom - this.margin.bottom]); 216 | 217 | this.elements.forEach(function(d){ 218 | d.show(this.svg, xScale, yScale); 219 | }, this); 220 | 221 | this.surroundings.forEach(function(d){ 222 | d[0].show(d[1]); 223 | }, this); 224 | 225 | this.drawAxis(xScale, yScale); 226 | if (this.xlabel_settings) { 227 | this._drawXLabel(this.xlabel_settings); 228 | } 229 | if (this.ylabel_settings) { 230 | this._drawYLabel(this.ylabel_settings); 231 | } 232 | }, 233 | 234 | drawAxis: function(xScale, yScale) { 235 | var xAxis = d3.svg.axis() 236 | .scale(xScale) 237 | .ticks(5) 238 | .orient("bottom"); 239 | 240 | var yAxis = d3.svg.axis() 241 | .scale(yScale) 242 | .ticks(5) 243 | .orient("left"); 244 | 245 | this.svg.append('g') 246 | .attr("class", "axis") 247 | .attr('transform', 'translate(0,'+(this.h-this.padding.bottom-this.margin.bottom)+')') 248 | .call(xAxis); 249 | this.svg.append('g') 250 | .attr("class", "axis") 251 | .attr('transform', 'translate('+(this.padding.left+this.margin.left)+', 0)') 252 | .call(yAxis); 253 | } 254 | }; 255 | 256 | /* Static methods */ 257 | Soba.getStyle = function(el, prop) { 258 | if (el.currentStyle) { 259 | return el.currentStyle[prop]; 260 | } else if (window.getComputedStyle) { 261 | return document.defaultView.getComputedStyle(el, null).getPropertyValue(prop); 262 | } 263 | return null; 264 | }; 265 | 266 | /* Sub classes */ 267 | Soba.Util = { 268 | arrayMax: function(arr) { 269 | return Math.max.apply(null, arr); 270 | }, 271 | arrayMin: function(arr) { 272 | return Math.min.apply(null, arr); 273 | }, 274 | parseColorOption: function(option) { 275 | if (!option) return 'blue'; 276 | var colors = { 277 | b: 'blue', 278 | g: 'green', 279 | r: 'red', 280 | c: 'cyan', 281 | m: 'magenta', 282 | y: 'yellow', 283 | k: 'black', 284 | w: 'white' 285 | } 286 | for (var key in colors) { 287 | if (option.indexOf(key) >= 0) { 288 | return colors[key]; 289 | } 290 | } 291 | return 'blue'; 292 | } 293 | }; 294 | 295 | Soba.Plot = function(x, y, option){ 296 | this.x = x; 297 | this.y = y; 298 | this.option = option; 299 | }; 300 | Soba.Plot.prototype = { 301 | x_range: function(){ 302 | if (!this._x_range) { 303 | this._x_range = ($M && this.x instanceof $M) ? [$M.min(this.x), $M.max(this.x)] : [Soba.Util.arrayMin(this.x), Soba.Util.arrayMax(this.x)]; 304 | } 305 | return this._x_range; 306 | }, 307 | y_range: function(){ 308 | if (!this._y_range){ 309 | this._y_range = ($M && this.y instanceof $M) ? [$M.min(this.y), $M.max(this.y)] : [Soba.Util.arrayMin(this.y), Soba.Util.arrayMax(this.y)]; 310 | } 311 | return this._y_range; 312 | }, 313 | show: function(svg, xScale, yScale){ 314 | var x = this.x, y = this.y, option = this.option; 315 | option = option ? option : ''; 316 | var xArray = ($M && x instanceof $M) ? $M.toArray(x) : x; 317 | 318 | var style = this._parsePlotOption(option); 319 | if (style.circle) { 320 | svg.append('g').selectAll("circle") 321 | .data(xArray) 322 | .enter() 323 | .append("circle") 324 | .attr("cx", function(d, i){ 325 | return xScale(d); 326 | }) 327 | .attr('cy', function(d, i){ 328 | return yScale( ($M && y instanceof $M) ? y.get(i,0) : y[i] ); 329 | }) 330 | .attr('fill', style.circle.fill) 331 | .attr('r', 2); 332 | } 333 | if (style.line) { 334 | var line = d3.svg.line() 335 | .x(function(d, i){ 336 | return xScale(d); 337 | }) 338 | .y(function(d, i){ 339 | return yScale( ($M && y instanceof $M) ? y.get(i,0) : y[i] ); 340 | }) 341 | .interpolate('linear'); 342 | 343 | var path = svg.append('path') 344 | .datum(xArray) 345 | .attr('d', line) 346 | .attr('fill', style.line.fill) 347 | .attr('stroke', style.line.stroke) 348 | .attr('stroke-width', 2); 349 | 350 | if (style.line.stroke_dasharray) { 351 | path.attr('stroke-dasharray', style.line.stroke_dasharray); 352 | } 353 | } 354 | }, 355 | 356 | drawLegend: function(g, title) { 357 | var style = this._parsePlotOption(this.option); 358 | var x_start=5, x_end = 35; 359 | var y = -5; 360 | if (style.circle) { 361 | for (var x=x_start ; x<=x_end ; x+=10) { 362 | g.append('circle') 363 | .attr('cx', x) 364 | .attr('cy', y) 365 | .attr('fill', style.circle.fill) 366 | .attr('r', 2) 367 | ; 368 | } 369 | } 370 | if (style.line) { 371 | var line = g.append('line') 372 | .attr('x1', x_start) 373 | .attr('y1', y) 374 | .attr('x2', x_end) 375 | .attr('y2', y) 376 | .attr('stroke', style.line.stroke) 377 | .attr('stroke-width', 2) 378 | ; 379 | if (style.line.stroke_dasharray) { 380 | line.attr('stroke-dasharray', style.line.stroke_dasharray); 381 | } 382 | } 383 | 384 | if (title) { 385 | var textarea = g.append('text').text(title) 386 | .attr('font-size', 10) 387 | .attr('x', x_end + 10) 388 | .attr('y', 0) 389 | ; 390 | 391 | var bbox = textarea.node().getBBox(); 392 | return bbox.x + bbox.width; 393 | } else { 394 | return x_end; 395 | } 396 | }, 397 | 398 | _parsePlotOption: function(option) { 399 | var res = { 400 | circle: null, 401 | line: null 402 | }; 403 | 404 | if (!option) option = 'b-'; 405 | 406 | if (option.indexOf('o') >= 0) { 407 | res.circle = { 408 | fill: this._parseColor(option), 409 | stroke: null 410 | }; 411 | } 412 | if (option.indexOf('-') >= 0 || option.indexOf(':') >= 0) { 413 | res.line = { 414 | fill: 'none', 415 | stroke: this._parseColor(option), 416 | stroke_dasharray: null 417 | }; 418 | if (option.indexOf('--') >= 0) { 419 | res.line.stroke_dasharray = '5,5'; 420 | } else if (option.indexOf('-.') >= 0) { 421 | res.line.stroke_dasharray = '4,6,2,6'; 422 | } else if (option.indexOf(':') >= 0) { 423 | res.line.stroke_dasharray = '2,3'; 424 | } 425 | } 426 | 427 | return res; 428 | }, 429 | 430 | _parseColor: function(option) { 431 | return Soba.Util.parseColorOption(option); 432 | }, 433 | }; 434 | 435 | Soba.Scatter = function(x, y, color){ 436 | this.x = x; 437 | this.y = y; 438 | this.color = color; 439 | }; 440 | Soba.Scatter.prototype = { 441 | x_range: function(){ 442 | if (!this._x_range) { 443 | this._x_range = ($M && this.x instanceof $M) ? [$M.min(this.x), $M.max(this.x)] : [Soba.Util.arrayMin(this.x), Soba.Util.arrayMax(this.x)]; 444 | } 445 | return this._x_range; 446 | }, 447 | y_range: function(){ 448 | if (!this._y_range){ 449 | this._y_range = ($M && this.y instanceof $M) ? [$M.min(this.y), $M.max(this.y)] : [Soba.Util.arrayMin(this.y), Soba.Util.arrayMax(this.y)]; 450 | } 451 | return this._y_range; 452 | }, 453 | show: function(svg, xScale, yScale){ 454 | var x = this.x, y = this.y, color = this.color; 455 | 456 | var color_list = d3.scale.category10(); 457 | 458 | var xArray = ($M && x instanceof $M) ? $M.toArray(x) : x; 459 | 460 | svg.append('g').selectAll("circle") 461 | .data(xArray) 462 | .enter() 463 | .append("circle") 464 | .attr("cx", function(d, i){ 465 | return xScale(d); 466 | }) 467 | .attr('cy', function(d, i){ 468 | return yScale( ($M && y instanceof $M) ? y.get(i,0) : y[i]); 469 | }) 470 | .attr('fill', function(d, i){ 471 | return ($M && color instanceof $M) ? color_list(color.get(i,0)) : color_list(1); 472 | }) 473 | .attr('r', 2); 474 | }, 475 | drawLegend: function(g, title){ 476 | var x = this.x, y = this.y, color = this.color; 477 | var color_list = d3.scale.category10(); 478 | 479 | var x_start=10, x_end = 30; 480 | var y = -3; 481 | 482 | var legend_color_list = []; 483 | if ($M && color instanceof $M) { 484 | var color_ = $M.toArray(color.t())[0]; 485 | var legend_color_list = color_.filter(function (x, i, self) { // Unique array 486 | return self.indexOf(x) === i; 487 | }); 488 | } else { 489 | var legend_color_list = [1]; 490 | } 491 | 492 | if (legend_color_list.length > 1) { 493 | for (var i=0 ; i val) mesh_min = val; 565 | xmap[ix][iy] = x; 566 | ymap[ix][iy] = y; 567 | } 568 | } 569 | this.mesh_min = mesh_min, this.mesh_max = mesh_max; 570 | 571 | // Determine levels 572 | var levels; 573 | if (args.levels) { 574 | levels = args.levels; 575 | } else { 576 | var n_levels = 10; 577 | var levels = new Array(n_levels); 578 | var level_min = mesh_min, level_max = mesh_max * 0.95; 579 | for (var i=0 ; i= x_bins-1 || iiy >= y_bins ) break; 662 | vertexes[0] = [xmap[iix ][iiy ], ymap[iix ][iiy ], mesh[iix ][iiy ]]; 663 | vertexes[1] = [xmap[iix+1][iiy ], ymap[iix+1][iiy ], mesh[iix+1][iiy ]]; 664 | vertexes[2] = [xmap[iix+1][iiy+1], ymap[iix+1][iiy+1], mesh[iix+1][iiy+1]]; 665 | vertexes[3] = [xmap[iix ][iiy+1], ymap[iix ][iiy+1], mesh[iix ][iiy+1]]; 666 | mark[iix][iiy] = true; 667 | var p = findPathInGrid(vertexes, level); 668 | if (p.length == 2) { 669 | var touching_ind = (valid_ind + 2) % 4; // e.g.) if valid_ind=1 (Right), touching_ind=3(Left). RIGHT edge of previous grid and LEFT grid of next grid are touching. 670 | if (p[0][2] == touching_ind) var next_point = p[1]; 671 | else if (p[1][2] == touching_ind) var next_point = p[0]; 672 | if (next_point) { 673 | points.push([next_point[0], next_point[1]]); 674 | valid_ind = next_point[2]; 675 | } else { 676 | valid_ind = -1; 677 | } 678 | } 679 | } 680 | } 681 | 682 | if (points.length >= 2){ 683 | var line = d3.svg.line() 684 | .x(function(d){ 685 | return xScale(d[0]); 686 | }) 687 | .y(function(d){ 688 | return yScale(d[1]); 689 | }) 690 | .interpolate('linear'); 691 | 692 | var path = svg.append('path') 693 | .datum(points) 694 | .attr('d', line) 695 | .attr('fill', 'none') 696 | .attr('stroke', level_color) 697 | .attr('stroke-width', 2); 698 | if (args.linestyles) { 699 | var linestyle; 700 | if (args.linestyles instanceof Array) { 701 | linestyle = args.linestyles[level_i % args.linestyles.length]; 702 | } else { 703 | linestyle = args.linestyles; 704 | } 705 | if (linestyle == 'dashed') { 706 | path.attr('stroke-dasharray', '3,3') 707 | } 708 | } 709 | } 710 | } 711 | } 712 | 713 | }, this); 714 | }, 715 | 716 | drawLegend: function(g, title) { 717 | var x_start=5, x_end = 35; 718 | var y = -5; 719 | var args = this.args; 720 | 721 | var domain = this.domain(); 722 | this.levels.forEach(function(level, level_i){ 723 | if (level_i > 0) return; //Todo 724 | 725 | if (args.colors) { 726 | var level_color = args.colors instanceof Array ? Soba.Util.parseColorOption(args.colors[level_i % args.colors.length]) : Soba.Util.parseColorOption(args.colors); 727 | } else { 728 | var level_color = this.color((level-domain[0])/(domain[1]-domain[0])); 729 | } 730 | 731 | var line = g.append('line') 732 | .attr('x1', x_start) 733 | .attr('y1', y) 734 | .attr('x2', x_end) 735 | .attr('y2', y) 736 | .attr('stroke', level_color) 737 | .attr('stroke-width', 2) 738 | ; 739 | if (args.linestyles) { 740 | var linestyle; 741 | if (args.linestyles instanceof Array) { 742 | linestyle = args.linestyles[level_i % args.linestyles.length]; 743 | } else { 744 | linestyle = args.linestyles; 745 | } 746 | if (linestyle == 'dashed') { 747 | line.attr('stroke-dasharray', '3,3') 748 | } 749 | } 750 | }, this); 751 | 752 | if (title) { 753 | var textarea = g.append('text').text(title) 754 | .attr('font-size', 10) 755 | .attr('x', x_end + 10) 756 | .attr('y', 0) 757 | ; 758 | 759 | var bbox = textarea.node().getBBox(); 760 | return bbox.x + bbox.width; 761 | } else { 762 | return x_end; 763 | } 764 | }, 765 | }; 766 | 767 | Soba.Label = function(title, options, orientation){ 768 | this.title = title; 769 | this.options = options ? options : {}; 770 | this.orientation = orientation ? orientation : 0; 771 | }; 772 | Soba.Label.prototype = { 773 | show: function(g){ 774 | var title = this.title; 775 | var options = this.options; 776 | var fontsize = options.fontsize ? options.fontsize : 20; 777 | 778 | var label = g.append('text') 779 | .text(title) 780 | .attr('dominant-baseline', 'text-before-edge') 781 | .attr('font-size', fontsize) 782 | ; 783 | 784 | var bbox = label.node().getBBox(); 785 | var label_w = bbox.width, label_h = bbox.height; 786 | var g_w = g.attr('width'), g_h = g.attr('height'); 787 | 788 | var rad = this.orientation * Math.PI / 180; 789 | var x = g_w/2 - label_w/2*Math.cos(rad) + label_h/2*Math.sin(rad); 790 | var y = g_h/2 - label_w/2*Math.sin(rad) - label_h/2*Math.cos(rad); 791 | label.attr('transform', 'translate('+x+','+y+') rotate('+this.orientation+')'); 792 | } 793 | }; 794 | 795 | Soba.Legend = function(elements, titles, location, padding, margin){ 796 | this.elements = elements; 797 | this.titles = titles; 798 | this.location = location; 799 | this.padding = padding; 800 | this.margin = margin; 801 | }; 802 | Soba.Legend.prototype = { 803 | show: function(svg){ 804 | var base = svg 805 | .append('g') 806 | ; 807 | 808 | var frame = base 809 | .append('rect') 810 | .attr('fill', 'white') 811 | .attr('stroke', 'black') 812 | ; 813 | 814 | var i = 0; 815 | var widths = []; 816 | this.elements.forEach(function(d){ 817 | if (d.drawLegend) { 818 | var x = 10; 819 | var y = 15 + i*15; 820 | var title = this.titles && this.titles[i] ? this.titles[i] : ''; 821 | var g = base.append('g').attr('transform', 'translate('+x+','+y+')'); 822 | var w = d.drawLegend(g, title); 823 | widths.push(w); 824 | i++; 825 | } 826 | }, this); 827 | var n_legends = i; 828 | 829 | var svg_w = svg.attr('width'), svg_h = svg.attr('height'); 830 | var max_width = widths.length > 0 ? Math.max.apply(null, widths) : 0; 831 | var frame_width = max_width + 20, frame_height = 15*n_legends+10; 832 | var legend_margin = 10; 833 | var legend_top = (svg_h - frame_height)/2; 834 | var legend_left = (svg_w - frame_width)/2; 835 | 836 | var legend_loc = this.location ? this.location : 'upper right'; 837 | if (legend_loc.indexOf('upper') >= 0) { 838 | legend_top = this.padding.top + this.margin.top + legend_margin; 839 | } else if (legend_loc.indexOf('bottom') >= 0) { 840 | legend_top = svg_h - this.padding.bottom - this.margin.bottom - frame_height - legend_margin; 841 | } 842 | if (legend_loc.indexOf('left') >= 0) { 843 | legend_left = this.padding.left + this.margin.left + legend_margin; 844 | } else if (legend_loc.indexOf('right') >= 0) { 845 | legend_left=svg_w - this.padding.right - this.margin.right - frame_width - legend_margin; 846 | } 847 | 848 | base 849 | .attr('transform', 'translate('+legend_left+','+legend_top+')') 850 | ; 851 | 852 | frame 853 | .attr('width', frame_width) 854 | .attr('height', frame_height) 855 | ; 856 | } 857 | }; 858 | 859 | Soba.Colorbar = function(contour_obj) { 860 | this.contour = contour_obj; 861 | Soba.Colorbar.count++; 862 | } 863 | Soba.Colorbar.count = 0; 864 | Soba.Colorbar.prototype = { 865 | show: function(g) { 866 | var g_w = g.attr('width'), g_h = g.attr('height'); 867 | 868 | var w = g_w - 30, h = g_h - 80; 869 | var x = 20, y = (g_h - h)/2; 870 | var base = g.append('g') 871 | .attr('transform', 'translate('+x+', '+y+')') 872 | ; 873 | 874 | var n_divs = 10; 875 | var color = this.contour.color; 876 | 877 | var scale = d3.scale.linear() 878 | .domain([0,n_divs]) 879 | .range([0, h]); 880 | 881 | var defs = g.append('svg:defs'); 882 | var id_prefix = 'cl_' + Soba.Colorbar.count + '_'; 883 | for (var i=0 ; i').attr('id', wrapper_div_id).addClass('soba_modal') 36 | .append( 37 | $('
').attr('id', title_div_id).addClass('title').text(title) 38 | .append( 39 | $('').attr('id', close_span_id).addClass('close').text('x') 40 | ) 41 | ) 42 | .append( 43 | $('
').attr('id', id).addClass('plotarea') 44 | ) 45 | ); 46 | // set css 47 | $('#' + id).css({ 48 | 'width' : width + 'px', 49 | 'height' : height + 'px', 50 | }); 51 | 52 | var wx, wy; 53 | wx = $(document).scrollLeft() + ($(window).width() - $('#' + wrapper_div_id).outerWidth()) / 2; 54 | if (wx < 0) wx = 0; 55 | wy = $(document).scrollTop() + ($(window).height() - $('#' + wrapper_div_id).outerHeight()) / 2; 56 | if (wy < 0) wy = 0; 57 | $('#' + wrapper_div_id).css({ top: wy, left: wx }); 58 | $('#' + close_span_id).click(function() {$('#' + wrapper_div_id).fadeOut(100);}); 59 | $('#' + title_div_id).mousedown(function(e) { 60 | var mx = e.pageX; 61 | var my = e.pageY; 62 | $(document).on('mousemove.' + wrapper_div_id, function(e) { 63 | wx += e.pageX - mx; 64 | wy += e.pageY - my; 65 | $('#' + wrapper_div_id).css({top: wy, left: wx}); 66 | mx = e.pageX; 67 | my = e.pageY; 68 | return false; 69 | }).one('mouseup', function(e) { 70 | $(document).off('mousemove.' + wrapper_div_id); 71 | }); 72 | return false; 73 | }); 74 | return id; 75 | }; 76 | }(); 77 | 78 | Soba.createModalPlot = function(width, height, title){ 79 | if (typeof width === 'undefined') width = 320; 80 | if (typeof height === 'undefined') height = 240; 81 | return new Soba("#" + Soba.createModal(width, height, title)); 82 | }; 83 | --------------------------------------------------------------------------------