├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── public ├── graphs │ ├── blocks_margin-convention.json │ ├── blocks_responding-to-resize.json │ ├── graph-data-structure_ab-nodes.json │ ├── graph-data-structure_ab.json │ ├── graph-data-structure_abc.json │ ├── graph-data-structure_cycle.json │ ├── graph-data-structure_cycles.json │ ├── graph-data-structure_getting-dressed.json │ ├── graph-data-structure_tricky-case.json │ ├── reactive-function_ab.json │ ├── reactive-function_abc.json │ ├── reactive-function_abcd.json │ ├── reactive-function_any-number.json │ ├── reactive-function_data-binding.json │ ├── reactive-function_full-name.json │ ├── reactive-function_ohms-law.json │ ├── reactive-function_tricky-case.json │ ├── reactive-model_ab.json │ ├── reactive-model_abc.json │ ├── reactive-model_full-name.json │ ├── reactive-model_not-too-tricky.json │ ├── reactive-model_side-effect-ab.json │ ├── reactive-model_side-effect.json │ ├── reactive-model_trickier-case.json │ ├── reactive-model_tricky-case.json │ ├── reactive-vis_margin-tweaked.json │ ├── reactive-vis_margin.json │ ├── reactive-vis_svg.json │ └── streamGraphExplorer.json ├── index.html └── reactiveFlowGraph │ ├── README.md │ ├── barChartFlow.json │ ├── forceDirectedGraph.js │ ├── index.html │ ├── main.js │ ├── model.js │ └── styles.css └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Datavis Tech 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # graph-diagrams 2 | A tool for visualizing directed graphs. [Try it!](http://bl.ocks.org/curran/9b73eb564c1c8a3d8f3ab207de364bf4) 3 | 4 | This tool has been used to create the graph diagrams found in 5 | 6 | * reactive-model 7 | * reactive-function 8 | * graph-data-structure 9 | 10 | To use, clone this repository then run `npm start`. To open a graph file from the `/public/graphs` directory, add the name of the file to the URL hash (e.g. [http://localhost:3000/#full-name](http://localhost:3000/#full-name)) then refresh the page. This is all the tool does currently. There are plans to save the coordinates for fixed nodes in the graph files. 11 | 12 | Draws from [Reactive Flow Diagram](http://bl.ocks.org/curran/5905182da50a4667dc00). 13 | 14 | ## Diagrams 15 | 16 |

17 | 18 |
19 | reactive-model - AB 20 |

21 |
22 |

23 | 24 |
25 | reactive-model - ABC 26 |

27 |
28 |

29 | 30 |
31 | reactive-model - CDE 32 |

33 |
34 |

35 | 36 |

37 |
38 |

39 | 40 |

41 |
42 |

43 | 44 |

45 |
46 |

47 | 48 |

49 |
50 |

51 | 52 |

53 |
54 |

55 | 56 |
57 | reactive-function - Ohm's Law 58 |

59 | 60 |
61 |

62 | 63 |
64 | bl.ocks.org - Responding to Resize with ReactiveModel 65 |

66 | 67 |
68 |

69 | 70 |
71 | bl.ocks.org - Margin Convention with ReactiveModel 72 |

73 | 74 |

75 | 76 |
77 | reactive-vis - SVG 78 |

79 | 80 |

81 | 82 |
83 | reactive-vis - Margin 84 |

85 | 86 |
87 |

88 | 89 |
90 | graph-data-structure - Getting Dressed 91 |

92 |
93 |

94 | 95 | 96 | 97 |

98 | 99 |

100 | 101 | 102 | 103 |

104 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // This module is a utility for writing graph files 2 | // from within the unit tests of other projects. 3 | var fs = require("fs"); 4 | module.exports = function (options){ 5 | return function (serialized, name){ 6 | if(options.outputGraphs){ 7 | var filename = [ 8 | "../graph-diagrams/public/graphs/", 9 | options.project, 10 | "_", 11 | name, 12 | ".json" 13 | ].join(""); 14 | var data = JSON.stringify(serialized, null, 2); 15 | fs.writeFile(filename, data); 16 | } 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graph-diagrams", 3 | "version": "0.5.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.3", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", 10 | "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", 11 | "dev": true, 12 | "requires": { 13 | "mime-types": "2.1.16", 14 | "negotiator": "0.6.1" 15 | } 16 | }, 17 | "array-flatten": { 18 | "version": "1.1.1", 19 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 20 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", 21 | "dev": true 22 | }, 23 | "content-disposition": { 24 | "version": "0.5.2", 25 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 26 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", 27 | "dev": true 28 | }, 29 | "content-type": { 30 | "version": "1.0.2", 31 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", 32 | "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", 33 | "dev": true 34 | }, 35 | "cookie": { 36 | "version": "0.3.1", 37 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 38 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", 39 | "dev": true 40 | }, 41 | "cookie-signature": { 42 | "version": "1.0.6", 43 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 44 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", 45 | "dev": true 46 | }, 47 | "debug": { 48 | "version": "2.6.8", 49 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", 50 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", 51 | "dev": true, 52 | "requires": { 53 | "ms": "2.0.0" 54 | } 55 | }, 56 | "depd": { 57 | "version": "1.1.1", 58 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 59 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", 60 | "dev": true 61 | }, 62 | "destroy": { 63 | "version": "1.0.4", 64 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 65 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 66 | "dev": true 67 | }, 68 | "ee-first": { 69 | "version": "1.1.1", 70 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 71 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 72 | "dev": true 73 | }, 74 | "encodeurl": { 75 | "version": "1.0.1", 76 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 77 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", 78 | "dev": true 79 | }, 80 | "escape-html": { 81 | "version": "1.0.3", 82 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 83 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 84 | "dev": true 85 | }, 86 | "etag": { 87 | "version": "1.8.0", 88 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", 89 | "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=", 90 | "dev": true 91 | }, 92 | "express": { 93 | "version": "4.15.4", 94 | "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", 95 | "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", 96 | "dev": true, 97 | "requires": { 98 | "accepts": "1.3.3", 99 | "array-flatten": "1.1.1", 100 | "content-disposition": "0.5.2", 101 | "content-type": "1.0.2", 102 | "cookie": "0.3.1", 103 | "cookie-signature": "1.0.6", 104 | "debug": "2.6.8", 105 | "depd": "1.1.1", 106 | "encodeurl": "1.0.1", 107 | "escape-html": "1.0.3", 108 | "etag": "1.8.0", 109 | "finalhandler": "1.0.4", 110 | "fresh": "0.5.0", 111 | "merge-descriptors": "1.0.1", 112 | "methods": "1.1.2", 113 | "on-finished": "2.3.0", 114 | "parseurl": "1.3.1", 115 | "path-to-regexp": "0.1.7", 116 | "proxy-addr": "1.1.5", 117 | "qs": "6.5.0", 118 | "range-parser": "1.2.0", 119 | "send": "0.15.4", 120 | "serve-static": "1.12.4", 121 | "setprototypeof": "1.0.3", 122 | "statuses": "1.3.1", 123 | "type-is": "1.6.15", 124 | "utils-merge": "1.0.0", 125 | "vary": "1.1.1" 126 | } 127 | }, 128 | "finalhandler": { 129 | "version": "1.0.4", 130 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz", 131 | "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==", 132 | "dev": true, 133 | "requires": { 134 | "debug": "2.6.8", 135 | "encodeurl": "1.0.1", 136 | "escape-html": "1.0.3", 137 | "on-finished": "2.3.0", 138 | "parseurl": "1.3.1", 139 | "statuses": "1.3.1", 140 | "unpipe": "1.0.0" 141 | } 142 | }, 143 | "forwarded": { 144 | "version": "0.1.0", 145 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", 146 | "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=", 147 | "dev": true 148 | }, 149 | "fresh": { 150 | "version": "0.5.0", 151 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", 152 | "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=", 153 | "dev": true 154 | }, 155 | "http-errors": { 156 | "version": "1.6.2", 157 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 158 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 159 | "dev": true, 160 | "requires": { 161 | "depd": "1.1.1", 162 | "inherits": "2.0.3", 163 | "setprototypeof": "1.0.3", 164 | "statuses": "1.3.1" 165 | } 166 | }, 167 | "inherits": { 168 | "version": "2.0.3", 169 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 170 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 171 | "dev": true 172 | }, 173 | "ipaddr.js": { 174 | "version": "1.4.0", 175 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", 176 | "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=", 177 | "dev": true 178 | }, 179 | "media-typer": { 180 | "version": "0.3.0", 181 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 182 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 183 | "dev": true 184 | }, 185 | "merge-descriptors": { 186 | "version": "1.0.1", 187 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 188 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", 189 | "dev": true 190 | }, 191 | "methods": { 192 | "version": "1.1.2", 193 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 194 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 195 | "dev": true 196 | }, 197 | "mime": { 198 | "version": "1.3.4", 199 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", 200 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", 201 | "dev": true 202 | }, 203 | "mime-db": { 204 | "version": "1.29.0", 205 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", 206 | "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=", 207 | "dev": true 208 | }, 209 | "mime-types": { 210 | "version": "2.1.16", 211 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", 212 | "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", 213 | "dev": true, 214 | "requires": { 215 | "mime-db": "1.29.0" 216 | } 217 | }, 218 | "ms": { 219 | "version": "2.0.0", 220 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 221 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 222 | "dev": true 223 | }, 224 | "negotiator": { 225 | "version": "0.6.1", 226 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 227 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", 228 | "dev": true 229 | }, 230 | "on-finished": { 231 | "version": "2.3.0", 232 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 233 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 234 | "dev": true, 235 | "requires": { 236 | "ee-first": "1.1.1" 237 | } 238 | }, 239 | "parseurl": { 240 | "version": "1.3.1", 241 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", 242 | "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", 243 | "dev": true 244 | }, 245 | "path-to-regexp": { 246 | "version": "0.1.7", 247 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 248 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", 249 | "dev": true 250 | }, 251 | "proxy-addr": { 252 | "version": "1.1.5", 253 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", 254 | "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", 255 | "dev": true, 256 | "requires": { 257 | "forwarded": "0.1.0", 258 | "ipaddr.js": "1.4.0" 259 | } 260 | }, 261 | "qs": { 262 | "version": "6.5.0", 263 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", 264 | "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==", 265 | "dev": true 266 | }, 267 | "range-parser": { 268 | "version": "1.2.0", 269 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 270 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", 271 | "dev": true 272 | }, 273 | "send": { 274 | "version": "0.15.4", 275 | "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", 276 | "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", 277 | "dev": true, 278 | "requires": { 279 | "debug": "2.6.8", 280 | "depd": "1.1.1", 281 | "destroy": "1.0.4", 282 | "encodeurl": "1.0.1", 283 | "escape-html": "1.0.3", 284 | "etag": "1.8.0", 285 | "fresh": "0.5.0", 286 | "http-errors": "1.6.2", 287 | "mime": "1.3.4", 288 | "ms": "2.0.0", 289 | "on-finished": "2.3.0", 290 | "range-parser": "1.2.0", 291 | "statuses": "1.3.1" 292 | } 293 | }, 294 | "serve-static": { 295 | "version": "1.12.4", 296 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", 297 | "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", 298 | "dev": true, 299 | "requires": { 300 | "encodeurl": "1.0.1", 301 | "escape-html": "1.0.3", 302 | "parseurl": "1.3.1", 303 | "send": "0.15.4" 304 | } 305 | }, 306 | "setprototypeof": { 307 | "version": "1.0.3", 308 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 309 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", 310 | "dev": true 311 | }, 312 | "statuses": { 313 | "version": "1.3.1", 314 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 315 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", 316 | "dev": true 317 | }, 318 | "type-is": { 319 | "version": "1.6.15", 320 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", 321 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 322 | "dev": true, 323 | "requires": { 324 | "media-typer": "0.3.0", 325 | "mime-types": "2.1.16" 326 | } 327 | }, 328 | "unpipe": { 329 | "version": "1.0.0", 330 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 331 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 332 | "dev": true 333 | }, 334 | "utils-merge": { 335 | "version": "1.0.0", 336 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", 337 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", 338 | "dev": true 339 | }, 340 | "vary": { 341 | "version": "1.1.1", 342 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", 343 | "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=", 344 | "dev": true 345 | } 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graph-diagrams", 3 | "version": "0.5.0", 4 | "description": "A tool for visualizing directed graphs.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "files": [ 11 | "index.js" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/datavis-tech/graph-diagrams.git" 16 | }, 17 | "keywords": [ 18 | "graph", 19 | "visualization" 20 | ], 21 | "author": "Curran Kelleher", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/datavis-tech/graph-diagrams/issues" 25 | }, 26 | "homepage": "https://github.com/datavis-tech/graph-diagrams#readme", 27 | "devDependencies": { 28 | "express": "^4.13.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/graphs/blocks_margin-convention.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "1", 5 | "propertyName": "svg-width" 6 | }, 7 | { 8 | "id": "2", 9 | "propertyName": "svg" 10 | }, 11 | { 12 | "id": "3", 13 | "propertyName": "width" 14 | }, 15 | { 16 | "id": "4", 17 | "propertyName": "svg-height" 18 | }, 19 | { 20 | "id": "5", 21 | "propertyName": "height" 22 | }, 23 | { 24 | "id": "6", 25 | "propertyName": "innerWidth" 26 | }, 27 | { 28 | "id": "7", 29 | "propertyName": "marginLeft" 30 | }, 31 | { 32 | "id": "8", 33 | "propertyName": "marginRight" 34 | }, 35 | { 36 | "id": "9", 37 | "propertyName": "innerHeight" 38 | }, 39 | { 40 | "id": "10", 41 | "propertyName": "marginTop" 42 | }, 43 | { 44 | "id": "11", 45 | "propertyName": "marginBottom" 46 | }, 47 | { 48 | "id": "12", 49 | "propertyName": "g" 50 | }, 51 | { 52 | "id": "13", 53 | "propertyName": "g-transform" 54 | }, 55 | { 56 | "id": "14", 57 | "propertyName": "rect" 58 | }, 59 | { 60 | "id": "15", 61 | "propertyName": "rect-width" 62 | }, 63 | { 64 | "id": "16", 65 | "propertyName": "rect-height" 66 | } 67 | ], 68 | "links": [ 69 | { 70 | "source": "2", 71 | "target": "1" 72 | }, 73 | { 74 | "source": "2", 75 | "target": "4" 76 | }, 77 | { 78 | "source": "2", 79 | "target": "12" 80 | }, 81 | { 82 | "source": "3", 83 | "target": "1" 84 | }, 85 | { 86 | "source": "3", 87 | "target": "6" 88 | }, 89 | { 90 | "source": "5", 91 | "target": "4" 92 | }, 93 | { 94 | "source": "5", 95 | "target": "9" 96 | }, 97 | { 98 | "source": "6", 99 | "target": "15" 100 | }, 101 | { 102 | "source": "7", 103 | "target": "6" 104 | }, 105 | { 106 | "source": "7", 107 | "target": "13" 108 | }, 109 | { 110 | "source": "8", 111 | "target": "6" 112 | }, 113 | { 114 | "source": "9", 115 | "target": "16" 116 | }, 117 | { 118 | "source": "10", 119 | "target": "9" 120 | }, 121 | { 122 | "source": "10", 123 | "target": "13" 124 | }, 125 | { 126 | "source": "11", 127 | "target": "9" 128 | }, 129 | { 130 | "source": "12", 131 | "target": "13" 132 | }, 133 | { 134 | "source": "12", 135 | "target": "14" 136 | }, 137 | { 138 | "source": "14", 139 | "target": "15" 140 | }, 141 | { 142 | "source": "14", 143 | "target": "16" 144 | } 145 | ] 146 | } -------------------------------------------------------------------------------- /public/graphs/blocks_responding-to-resize.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "1", 5 | "propertyName": "" 6 | }, 7 | { 8 | "id": "2", 9 | "propertyName": "svg" 10 | }, 11 | { 12 | "id": "3", 13 | "propertyName": "width" 14 | }, 15 | { 16 | "id": "4", 17 | "propertyName": "height" 18 | } 19 | ], 20 | "links": [ 21 | { 22 | "source": "2", 23 | "target": "1" 24 | }, 25 | { 26 | "source": "3", 27 | "target": "1" 28 | }, 29 | { 30 | "source": "4", 31 | "target": "1" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /public/graphs/graph-data-structure_ab-nodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "a" 5 | }, 6 | { 7 | "id": "b" 8 | } 9 | ], 10 | "links": [] 11 | } -------------------------------------------------------------------------------- /public/graphs/graph-data-structure_ab.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "a" 5 | }, 6 | { 7 | "id": "b" 8 | } 9 | ], 10 | "links": [ 11 | { 12 | "source": "a", 13 | "target": "b" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /public/graphs/graph-data-structure_abc.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "a" 5 | }, 6 | { 7 | "id": "b" 8 | }, 9 | { 10 | "id": "c" 11 | } 12 | ], 13 | "links": [ 14 | { 15 | "source": "a", 16 | "target": "b" 17 | }, 18 | { 19 | "source": "b", 20 | "target": "c" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /public/graphs/graph-data-structure_cycle.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "a" 5 | }, 6 | { 7 | "id": "b" 8 | }, 9 | { 10 | "id": "c" 11 | } 12 | ], 13 | "links": [ 14 | { 15 | "source": "a", 16 | "target": "b" 17 | }, 18 | { 19 | "source": "b", 20 | "target": "c" 21 | }, 22 | { 23 | "source": "c", 24 | "target": "a" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /public/graphs/graph-data-structure_cycles.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "a" 5 | }, 6 | { 7 | "id": "b" 8 | }, 9 | { 10 | "id": "c" 11 | } 12 | ], 13 | "links": [ 14 | { 15 | "source": "a", 16 | "target": "b" 17 | }, 18 | { 19 | "source": "a", 20 | "target": "c" 21 | }, 22 | { 23 | "source": "b", 24 | "target": "a" 25 | }, 26 | { 27 | "source": "b", 28 | "target": "c" 29 | }, 30 | { 31 | "source": "c", 32 | "target": "b" 33 | }, 34 | { 35 | "source": "c", 36 | "target": "a" 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /public/graphs/graph-data-structure_getting-dressed.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "socks" 5 | }, 6 | { 7 | "id": "shoes" 8 | }, 9 | { 10 | "id": "shirt" 11 | }, 12 | { 13 | "id": "belt" 14 | }, 15 | { 16 | "id": "tie" 17 | }, 18 | { 19 | "id": "jacket" 20 | }, 21 | { 22 | "id": "pants" 23 | }, 24 | { 25 | "id": "underpants" 26 | } 27 | ], 28 | "links": [ 29 | { 30 | "source": "socks", 31 | "target": "shoes" 32 | }, 33 | { 34 | "source": "shirt", 35 | "target": "belt" 36 | }, 37 | { 38 | "source": "shirt", 39 | "target": "tie" 40 | }, 41 | { 42 | "source": "belt", 43 | "target": "jacket" 44 | }, 45 | { 46 | "source": "tie", 47 | "target": "jacket" 48 | }, 49 | { 50 | "source": "pants", 51 | "target": "shoes" 52 | }, 53 | { 54 | "source": "pants", 55 | "target": "belt" 56 | }, 57 | { 58 | "source": "underpants", 59 | "target": "pants" 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /public/graphs/graph-data-structure_tricky-case.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "a" 5 | }, 6 | { 7 | "id": "b" 8 | }, 9 | { 10 | "id": "d" 11 | }, 12 | { 13 | "id": "c" 14 | }, 15 | { 16 | "id": "e" 17 | } 18 | ], 19 | "links": [ 20 | { 21 | "source": "a", 22 | "target": "b" 23 | }, 24 | { 25 | "source": "a", 26 | "target": "d" 27 | }, 28 | { 29 | "source": "b", 30 | "target": "c" 31 | }, 32 | { 33 | "source": "d", 34 | "target": "e" 35 | }, 36 | { 37 | "source": "c", 38 | "target": "e" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /public/graphs/reactive-function_ab.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "40", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "41", 9 | "propertyName": "a" 10 | } 11 | ], 12 | "links": [ 13 | { 14 | "source": "41", 15 | "target": "40" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /public/graphs/reactive-function_abc.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "9", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "10", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "11", 13 | "propertyName": "c" 14 | } 15 | ], 16 | "links": [ 17 | { 18 | "source": "9", 19 | "target": "11" 20 | }, 21 | { 22 | "source": "10", 23 | "target": "9" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /public/graphs/reactive-function_abcd.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "12", 5 | "propertyName": "c" 6 | }, 7 | { 8 | "id": "13", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "14", 13 | "propertyName": "d" 14 | }, 15 | { 16 | "id": "15", 17 | "propertyName": "b" 18 | } 19 | ], 20 | "links": [ 21 | { 22 | "source": "12", 23 | "target": "14" 24 | }, 25 | { 26 | "source": "13", 27 | "target": "12" 28 | }, 29 | { 30 | "source": "15", 31 | "target": "14" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /public/graphs/reactive-function_any-number.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "4", 5 | "propertyName": "d" 6 | }, 7 | { 8 | "id": "5", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "6", 13 | "propertyName": "e" 14 | }, 15 | { 16 | "id": "7", 17 | "propertyName": "b" 18 | }, 19 | { 20 | "id": "8", 21 | "propertyName": "c" 22 | } 23 | ], 24 | "links": [ 25 | { 26 | "source": "5", 27 | "target": "4" 28 | }, 29 | { 30 | "source": "5", 31 | "target": "6" 32 | }, 33 | { 34 | "source": "7", 35 | "target": "6" 36 | }, 37 | { 38 | "source": "8", 39 | "target": "6" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /public/graphs/reactive-function_data-binding.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "42", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "43", 9 | "propertyName": "a" 10 | } 11 | ], 12 | "links": [ 13 | { 14 | "source": "42", 15 | "target": "43" 16 | }, 17 | { 18 | "source": "43", 19 | "target": "42" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /public/graphs/reactive-function_full-name.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "1", 5 | "propertyName": "fullName" 6 | }, 7 | { 8 | "id": "2", 9 | "propertyName": "firstName" 10 | }, 11 | { 12 | "id": "3", 13 | "propertyName": "lastName" 14 | } 15 | ], 16 | "links": [ 17 | { 18 | "source": "2", 19 | "target": "1" 20 | }, 21 | { 22 | "source": "3", 23 | "target": "1" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /public/graphs/reactive-function_ohms-law.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "44", 5 | "propertyName": "I" 6 | }, 7 | { 8 | "id": "45", 9 | "propertyName": "V" 10 | }, 11 | { 12 | "id": "46", 13 | "propertyName": "R" 14 | } 15 | ], 16 | "links": [ 17 | { 18 | "source": "44", 19 | "target": "45" 20 | }, 21 | { 22 | "source": "44", 23 | "target": "46" 24 | }, 25 | { 26 | "source": "45", 27 | "target": "44" 28 | }, 29 | { 30 | "source": "45", 31 | "target": "46" 32 | }, 33 | { 34 | "source": "46", 35 | "target": "44" 36 | }, 37 | { 38 | "source": "46", 39 | "target": "45" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /public/graphs/reactive-function_tricky-case.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "16", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "17", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "18", 13 | "propertyName": "c" 14 | }, 15 | { 16 | "id": "19", 17 | "propertyName": "d" 18 | }, 19 | { 20 | "id": "20", 21 | "propertyName": "e" 22 | } 23 | ], 24 | "links": [ 25 | { 26 | "source": "16", 27 | "target": "18" 28 | }, 29 | { 30 | "source": "17", 31 | "target": "16" 32 | }, 33 | { 34 | "source": "17", 35 | "target": "19" 36 | }, 37 | { 38 | "source": "18", 39 | "target": "20" 40 | }, 41 | { 42 | "source": "19", 43 | "target": "20" 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /public/graphs/reactive-model_ab.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "34", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "35", 9 | "propertyName": "a" 10 | } 11 | ], 12 | "links": [ 13 | { 14 | "source": "35", 15 | "target": "34" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /public/graphs/reactive-model_abc.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "46", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "47", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "48", 13 | "propertyName": "c" 14 | } 15 | ], 16 | "links": [ 17 | { 18 | "source": "46", 19 | "target": "48" 20 | }, 21 | { 22 | "source": "47", 23 | "target": "46" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /public/graphs/reactive-model_full-name.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "40", 5 | "propertyName": "fullName" 6 | }, 7 | { 8 | "id": "41", 9 | "propertyName": "firstName" 10 | }, 11 | { 12 | "id": "42", 13 | "propertyName": "lastName" 14 | } 15 | ], 16 | "links": [ 17 | { 18 | "source": "41", 19 | "target": "40" 20 | }, 21 | { 22 | "source": "42", 23 | "target": "40" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /public/graphs/reactive-model_not-too-tricky.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "49", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "50", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "51", 13 | "propertyName": "d" 14 | }, 15 | { 16 | "id": "52", 17 | "propertyName": "c" 18 | }, 19 | { 20 | "id": "53", 21 | "propertyName": "e" 22 | } 23 | ], 24 | "links": [ 25 | { 26 | "source": "49", 27 | "target": "53" 28 | }, 29 | { 30 | "source": "50", 31 | "target": "49" 32 | }, 33 | { 34 | "source": "51", 35 | "target": "53" 36 | }, 37 | { 38 | "source": "52", 39 | "target": "51" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /public/graphs/reactive-model_side-effect-ab.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "89", 5 | "propertyName": "" 6 | }, 7 | { 8 | "id": "90", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "91", 13 | "propertyName": "b" 14 | } 15 | ], 16 | "links": [ 17 | { 18 | "source": "90", 19 | "target": "89" 20 | }, 21 | { 22 | "source": "91", 23 | "target": "89" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /public/graphs/reactive-model_side-effect.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "87", 5 | "propertyName": "" 6 | }, 7 | { 8 | "id": "88", 9 | "propertyName": "a" 10 | } 11 | ], 12 | "links": [ 13 | { 14 | "source": "88", 15 | "target": "87" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /public/graphs/reactive-model_trickier-case.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "59", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "60", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "61", 13 | "propertyName": "c" 14 | }, 15 | { 16 | "id": "62", 17 | "propertyName": "d" 18 | }, 19 | { 20 | "id": "63", 21 | "propertyName": "e" 22 | }, 23 | { 24 | "id": "64", 25 | "propertyName": "f" 26 | }, 27 | { 28 | "id": "65", 29 | "propertyName": "g" 30 | }, 31 | { 32 | "id": "66", 33 | "propertyName": "h" 34 | } 35 | ], 36 | "links": [ 37 | { 38 | "source": "59", 39 | "target": "61" 40 | }, 41 | { 42 | "source": "60", 43 | "target": "59" 44 | }, 45 | { 46 | "source": "60", 47 | "target": "63" 48 | }, 49 | { 50 | "source": "60", 51 | "target": "65" 52 | }, 53 | { 54 | "source": "61", 55 | "target": "62" 56 | }, 57 | { 58 | "source": "62", 59 | "target": "66" 60 | }, 61 | { 62 | "source": "63", 63 | "target": "64" 64 | }, 65 | { 66 | "source": "64", 67 | "target": "66" 68 | }, 69 | { 70 | "source": "65", 71 | "target": "66" 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /public/graphs/reactive-model_tricky-case.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "54", 5 | "propertyName": "b" 6 | }, 7 | { 8 | "id": "55", 9 | "propertyName": "a" 10 | }, 11 | { 12 | "id": "56", 13 | "propertyName": "c" 14 | }, 15 | { 16 | "id": "57", 17 | "propertyName": "d" 18 | }, 19 | { 20 | "id": "58", 21 | "propertyName": "e" 22 | } 23 | ], 24 | "links": [ 25 | { 26 | "source": "54", 27 | "target": "56" 28 | }, 29 | { 30 | "source": "54", 31 | "target": "58" 32 | }, 33 | { 34 | "source": "55", 35 | "target": "54" 36 | }, 37 | { 38 | "source": "55", 39 | "target": "57" 40 | }, 41 | { 42 | "source": "57", 43 | "target": "58" 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /public/graphs/reactive-vis_margin-tweaked.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "7", 5 | "propertyName": "svg", 6 | "faded": true 7 | }, 8 | { 9 | "id": "8", 10 | "propertyName": "width", 11 | "faded": true 12 | }, 13 | { 14 | "id": "10", 15 | "propertyName": "height", 16 | "faded": true 17 | }, 18 | { 19 | "id": "11", 20 | "propertyName": "innerWidth" 21 | }, 22 | { 23 | "id": "12", 24 | "propertyName": "marginLeft" 25 | }, 26 | { 27 | "id": "13", 28 | "propertyName": "marginRight" 29 | }, 30 | { 31 | "id": "14", 32 | "propertyName": "innerHeight" 33 | }, 34 | { 35 | "id": "15", 36 | "propertyName": "marginTop" 37 | }, 38 | { 39 | "id": "16", 40 | "propertyName": "marginBottom" 41 | }, 42 | { 43 | "id": "17", 44 | "propertyName": "g" 45 | }, 46 | { 47 | "id": "18", 48 | "propertyName": "g-transform" 49 | } 50 | ], 51 | "links": [ 52 | { 53 | "source": "7", 54 | "target": "17" 55 | }, 56 | { 57 | "source": "8", 58 | "target": "11" 59 | }, 60 | { 61 | "source": "10", 62 | "target": "14" 63 | }, 64 | { 65 | "source": "12", 66 | "target": "11" 67 | }, 68 | { 69 | "source": "12", 70 | "target": "18" 71 | }, 72 | { 73 | "source": "13", 74 | "target": "11" 75 | }, 76 | { 77 | "source": "15", 78 | "target": "14" 79 | }, 80 | { 81 | "source": "15", 82 | "target": "18" 83 | }, 84 | { 85 | "source": "16", 86 | "target": "14" 87 | }, 88 | { 89 | "source": "17", 90 | "target": "18" 91 | } 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /public/graphs/reactive-vis_margin.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "7", 5 | "propertyName": "svg", 6 | "faded": true 7 | }, 8 | { 9 | "id": "8", 10 | "propertyName": "width", 11 | "faded": true 12 | }, 13 | { 14 | "id": "10", 15 | "propertyName": "height", 16 | "faded": true 17 | }, 18 | { 19 | "id": "11", 20 | "propertyName": "innerWidth" 21 | }, 22 | { 23 | "id": "12", 24 | "propertyName": "marginLeft" 25 | }, 26 | { 27 | "id": "13", 28 | "propertyName": "marginRight" 29 | }, 30 | { 31 | "id": "14", 32 | "propertyName": "innerHeight" 33 | }, 34 | { 35 | "id": "15", 36 | "propertyName": "marginTop" 37 | }, 38 | { 39 | "id": "16", 40 | "propertyName": "marginBottom" 41 | }, 42 | { 43 | "id": "17", 44 | "propertyName": "g" 45 | }, 46 | { 47 | "id": "18", 48 | "propertyName": "g-transform" 49 | } 50 | ], 51 | "links": [ 52 | { 53 | "source": "7", 54 | "target": "17" 55 | }, 56 | { 57 | "source": "8", 58 | "target": "11" 59 | }, 60 | { 61 | "source": "10", 62 | "target": "14" 63 | }, 64 | { 65 | "source": "12", 66 | "target": "11" 67 | }, 68 | { 69 | "source": "12", 70 | "target": "18" 71 | }, 72 | { 73 | "source": "13", 74 | "target": "11" 75 | }, 76 | { 77 | "source": "15", 78 | "target": "14" 79 | }, 80 | { 81 | "source": "15", 82 | "target": "18" 83 | }, 84 | { 85 | "source": "16", 86 | "target": "14" 87 | }, 88 | { 89 | "source": "17", 90 | "target": "18" 91 | } 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /public/graphs/reactive-vis_svg.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "1", 5 | "propertyName": "svg-width" 6 | }, 7 | { 8 | "id": "2", 9 | "propertyName": "svg" 10 | }, 11 | { 12 | "id": "3", 13 | "propertyName": "width" 14 | }, 15 | { 16 | "id": "4", 17 | "propertyName": "svg-height" 18 | }, 19 | { 20 | "id": "5", 21 | "propertyName": "height" 22 | } 23 | ], 24 | "links": [ 25 | { 26 | "source": "2", 27 | "target": "1" 28 | }, 29 | { 30 | "source": "2", 31 | "target": "4" 32 | }, 33 | { 34 | "source": "3", 35 | "target": "1" 36 | }, 37 | { 38 | "source": "5", 39 | "target": "4" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /public/graphs/streamGraphExplorer.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "1", 5 | "propertyName": "data" 6 | }, 7 | { 8 | "id": "2", 9 | "propertyName": "packedData" 10 | }, 11 | { 12 | "id": "3", 13 | "propertyName": "dataFiltered" 14 | }, 15 | { 16 | "id": "4", 17 | "propertyName": "src" 18 | }, 19 | { 20 | "id": "5", 21 | "propertyName": "dest" 22 | }, 23 | { 24 | "id": "6", 25 | "propertyName": "dataBySrc" 26 | }, 27 | { 28 | "id": "7", 29 | "propertyName": "dataByDest" 30 | }, 31 | { 32 | "id": "8", 33 | "propertyName": "dataByYear" 34 | }, 35 | { 36 | "id": "9", 37 | "propertyName": "contextStreamData" 38 | }, 39 | { 40 | "id": "10", 41 | "propertyName": "allYears" 42 | }, 43 | { 44 | "id": "11", 45 | "propertyName": "srcKeys" 46 | }, 47 | { 48 | "id": "12", 49 | "propertyName": "maxStreamLayers" 50 | }, 51 | { 52 | "id": "13", 53 | "propertyName": "minStreamMax" 54 | }, 55 | { 56 | "id": "14", 57 | "propertyName": "destKeys" 58 | }, 59 | { 60 | "id": "15", 61 | "propertyName": "srcStreamDataAllYears" 62 | }, 63 | { 64 | "id": "16", 65 | "propertyName": "destStreamDataAllYears" 66 | }, 67 | { 68 | "id": "17", 69 | "propertyName": "srcStreamData" 70 | }, 71 | { 72 | "id": "18", 73 | "propertyName": "zoomExtent" 74 | }, 75 | { 76 | "id": "19", 77 | "propertyName": "destStreamData" 78 | }, 79 | { 80 | "id": "20", 81 | "propertyName": "hashRouting" 82 | }, 83 | { 84 | "id": "21", 85 | "propertyName": "svgDimensions" 86 | }, 87 | { 88 | "id": "22", 89 | "propertyName": "containerBox" 90 | }, 91 | { 92 | "id": "23", 93 | "propertyName": "srcStream" 94 | }, 95 | { 96 | "id": "24", 97 | "propertyName": "srcStreamBox" 98 | }, 99 | { 100 | "id": "25", 101 | "propertyName": "streamsMargin" 102 | }, 103 | { 104 | "id": "26", 105 | "propertyName": "showStreamLabels" 106 | }, 107 | { 108 | "id": "27", 109 | "propertyName": "colorScale" 110 | }, 111 | { 112 | "id": "28", 113 | "propertyName": "destStream" 114 | }, 115 | { 116 | "id": "29", 117 | "propertyName": "destStreamBox" 118 | }, 119 | { 120 | "id": "30", 121 | "propertyName": "timeTicksYExtent" 122 | }, 123 | { 124 | "id": "31", 125 | "propertyName": "timePanel" 126 | }, 127 | { 128 | "id": "32", 129 | "propertyName": "timePanelBox" 130 | }, 131 | { 132 | "id": "33", 133 | "propertyName": "contextStream" 134 | }, 135 | { 136 | "id": "34", 137 | "propertyName": "contextStreamBox" 138 | }, 139 | { 140 | "id": "35", 141 | "propertyName": "timeExtent" 142 | } 143 | ], 144 | "links": [ 145 | { 146 | "source": "1", 147 | "target": "3" 148 | }, 149 | { 150 | "source": "2", 151 | "target": "1" 152 | }, 153 | { 154 | "source": "3", 155 | "target": "6" 156 | }, 157 | { 158 | "source": "3", 159 | "target": "7" 160 | }, 161 | { 162 | "source": "3", 163 | "target": "8" 164 | }, 165 | { 166 | "source": "4", 167 | "target": "3" 168 | }, 169 | { 170 | "source": "4", 171 | "target": "20" 172 | }, 173 | { 174 | "source": "5", 175 | "target": "3" 176 | }, 177 | { 178 | "source": "5", 179 | "target": "20" 180 | }, 181 | { 182 | "source": "6", 183 | "target": "11" 184 | }, 185 | { 186 | "source": "6", 187 | "target": "15" 188 | }, 189 | { 190 | "source": "7", 191 | "target": "14" 192 | }, 193 | { 194 | "source": "7", 195 | "target": "16" 196 | }, 197 | { 198 | "source": "8", 199 | "target": "9" 200 | }, 201 | { 202 | "source": "9", 203 | "target": "33" 204 | }, 205 | { 206 | "source": "10", 207 | "target": "9" 208 | }, 209 | { 210 | "source": "10", 211 | "target": "15" 212 | }, 213 | { 214 | "source": "10", 215 | "target": "16" 216 | }, 217 | { 218 | "source": "11", 219 | "target": "23" 220 | }, 221 | { 222 | "source": "12", 223 | "target": "11" 224 | }, 225 | { 226 | "source": "12", 227 | "target": "14" 228 | }, 229 | { 230 | "source": "13", 231 | "target": "11" 232 | }, 233 | { 234 | "source": "13", 235 | "target": "14" 236 | }, 237 | { 238 | "source": "14", 239 | "target": "28" 240 | }, 241 | { 242 | "source": "15", 243 | "target": "17" 244 | }, 245 | { 246 | "source": "16", 247 | "target": "19" 248 | }, 249 | { 250 | "source": "17", 251 | "target": "23" 252 | }, 253 | { 254 | "source": "18", 255 | "target": "17" 256 | }, 257 | { 258 | "source": "18", 259 | "target": "19" 260 | }, 261 | { 262 | "source": "18", 263 | "target": "20" 264 | }, 265 | { 266 | "source": "18", 267 | "target": "23" 268 | }, 269 | { 270 | "source": "18", 271 | "target": "28" 272 | }, 273 | { 274 | "source": "18", 275 | "target": "31" 276 | }, 277 | { 278 | "source": "19", 279 | "target": "28" 280 | }, 281 | { 282 | "source": "22", 283 | "target": "21" 284 | }, 285 | { 286 | "source": "24", 287 | "target": "23" 288 | }, 289 | { 290 | "source": "24", 291 | "target": "30" 292 | }, 293 | { 294 | "source": "25", 295 | "target": "23" 296 | }, 297 | { 298 | "source": "25", 299 | "target": "28" 300 | }, 301 | { 302 | "source": "25", 303 | "target": "31" 304 | }, 305 | { 306 | "source": "26", 307 | "target": "23" 308 | }, 309 | { 310 | "source": "26", 311 | "target": "28" 312 | }, 313 | { 314 | "source": "27", 315 | "target": "23" 316 | }, 317 | { 318 | "source": "27", 319 | "target": "28" 320 | }, 321 | { 322 | "source": "29", 323 | "target": "28" 324 | }, 325 | { 326 | "source": "29", 327 | "target": "30" 328 | }, 329 | { 330 | "source": "30", 331 | "target": "31" 332 | }, 333 | { 334 | "source": "32", 335 | "target": "31" 336 | }, 337 | { 338 | "source": "34", 339 | "target": "33" 340 | }, 341 | { 342 | "source": "35", 343 | "target": "33" 344 | } 345 | ] 346 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 19 | 20 | 21 | Graph Editor 22 | 23 | 24 | 56 | 57 | 58 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /public/reactiveFlowGraph/README.md: -------------------------------------------------------------------------------- 1 | This program renders a network diagram for a [ModelJS](https://github.com/curran/model) reactive flow. 2 | 3 | The input data is generated by an [experimental ModelJS branch](https://github.com/curran/model/tree/flow-graph-detection) that computes the reactive flow graph at runtime. 4 | 5 | Based on a [previous implementation from July 2014](https://github.com/curran/model/tree/5a30cc7647d970e452bbe10ef458c40a25d825c6/examples/dataFlowDiagram) 6 | -------------------------------------------------------------------------------- /public/reactiveFlowGraph/barChartFlow.json: -------------------------------------------------------------------------------- 1 | {"nodes":[{"type":"lambda","fixed":1,"x":-108,"y":308},{"type":"property","property":"container","fixed":1,"x":-233,"y":306},{"type":"property","property":"svg","fixed":1,"x":-5,"y":309},{"type":"lambda","fixed":1,"x":214,"y":176},{"type":"property","property":"box","fixed":1,"x":-200,"y":257},{"type":"lambda","fixed":1,"x":80,"y":311},{"type":"property","property":"g","fixed":1,"x":165,"y":311},{"type":"lambda","fixed":1,"x":212,"y":264},{"type":"property","property":"margin","fixed":1,"x":-222,"y":202},{"type":"lambda","fixed":1,"x":231,"y":369},{"type":"property","property":"titleText","fixed":1,"x":388,"y":347},{"type":"lambda","fixed":1,"x":578,"y":391},{"type":"property","property":"titleOffset","fixed":1,"x":430,"y":401},{"type":"lambda","fixed":1,"x":214,"y":216},{"type":"property","property":"width","fixed":1,"x":497,"y":63},{"type":"property","property":"height","fixed":1,"x":485,"y":555},{"type":"lambda","fixed":1,"x":653,"y":123},{"type":"property","property":"xAxisG","fixed":1,"x":782,"y":65},{"type":"property","property":"xAxisText","fixed":1,"x":785,"y":119},{"type":"lambda","fixed":1,"x":963,"y":159},{"type":"property","property":"xAxisLabelOffset","fixed":1,"x":-292,"y":153},{"type":"lambda","fixed":1,"x":955,"y":53},{"type":"lambda","fixed":1,"x":963,"y":107},{"type":"lambda","fixed":1,"x":963,"y":221},{"type":"property","property":"xAxisLabel","fixed":1,"x":-257,"y":104},{"type":"lambda","fixed":1,"x":598,"y":332},{"type":"property","property":"yAxisG","fixed":1,"x":811,"y":442},{"type":"property","property":"yAxisText","fixed":1,"x":790,"y":390},{"type":"lambda","fixed":1,"x":946,"y":335},{"type":"property","property":"yAxisLabelOffset","fixed":1,"x":-286,"y":418},{"type":"lambda","fixed":1,"x":946,"y":463},{"type":"lambda","fixed":1,"x":944,"y":407},{"type":"property","property":"yAxisLabel","fixed":1,"x":-248,"y":466},{"type":"lambda","fixed":1,"x":674,"y":271},{"type":"property","property":"barsG","fixed":1,"x":1133,"y":312},{"type":"lambda","fixed":1,"x":589,"y":235},{"type":"lambda","fixed":true,"x":-87,"y":137},{"type":"property","property":"data","fixed":1,"x":-203,"y":365},{"type":"property","property":"xAttribute","fixed":1,"x":-242,"y":54},{"type":"property","property":"getX","fixed":1,"x":147,"y":92},{"type":"lambda","fixed":1,"x":17,"y":26},{"type":"property","property":"sortField","fixed":1,"x":-234,"y":2},{"type":"property","property":"sortOrder","fixed":1,"x":-235,"y":-51},{"type":"property","property":"sortedData","fixed":1,"x":157,"y":17},{"type":"lambda","fixed":1,"x":-70,"y":499},{"type":"property","property":"yAttribute","fixed":1,"x":-233,"y":521},{"type":"property","property":"getY","fixed":1,"x":61,"y":525},{"type":"lambda","fixed":1,"x":281,"y":585},{"type":"property","property":"yDomainMin","fixed":1,"x":-252,"y":573},{"type":"property","property":"yDomainMax","fixed":1,"x":-255,"y":625},{"type":"property","property":"yDomain","fixed":1,"x":475,"y":614},{"type":"lambda","fixed":1,"x":678,"y":566},{"type":"property","property":"yScale","fixed":1,"x":815,"y":565},{"type":"lambda","fixed":1,"x":1033,"y":516},{"type":"property","property":"getYScaled","fixed":1,"x":1243,"y":482},{"type":"lambda","fixed":1,"x":326,"y":4},{"type":"property","property":"xDomain","fixed":1,"x":498,"y":-11},{"type":"lambda","fixed":1,"x":952,"y":573},{"type":"lambda","fixed":1,"x":668,"y":-27},{"type":"property","property":"barPadding","fixed":1,"x":-248,"y":-101},{"type":"property","property":"xScale","fixed":1,"x":787,"y":0},{"type":"lambda","fixed":1,"x":1092,"y":96},{"type":"property","property":"getXScaled","fixed":1,"x":1233,"y":131},{"type":"lambda","fixed":1,"x":955,"y":-2},{"type":"lambda","fixed":1,"x":1378,"y":318}],"links":[{"source":1,"target":0},{"source":0,"target":2},{"source":2,"target":3},{"source":4,"target":3},{"source":2,"target":5},{"source":5,"target":6},{"source":6,"target":7},{"source":8,"target":7},{"source":6,"target":9},{"source":9,"target":10},{"source":10,"target":11},{"source":12,"target":11},{"source":4,"target":13},{"source":8,"target":13},{"source":13,"target":14},{"source":13,"target":15},{"source":6,"target":16},{"source":16,"target":17},{"source":16,"target":18},{"source":18,"target":19},{"source":20,"target":19},{"source":17,"target":21},{"source":15,"target":21},{"source":18,"target":22},{"source":14,"target":22},{"source":18,"target":23},{"source":24,"target":23},{"source":6,"target":25},{"source":25,"target":26},{"source":25,"target":27},{"source":27,"target":28},{"source":29,"target":28},{"source":27,"target":30},{"source":15,"target":30},{"source":27,"target":31},{"source":32,"target":31},{"source":6,"target":33},{"source":33,"target":34},{"source":10,"target":35},{"source":14,"target":35},{"source":37,"target":36},{"source":38,"target":36},{"source":36,"target":39},{"source":41,"target":40},{"source":42,"target":40},{"source":37,"target":40},{"source":40,"target":43},{"source":37,"target":44},{"source":45,"target":44},{"source":44,"target":46},{"source":37,"target":47},{"source":46,"target":47},{"source":48,"target":47},{"source":49,"target":47},{"source":47,"target":50},{"source":37,"target":51},{"source":50,"target":51},{"source":15,"target":51},{"source":51,"target":52},{"source":37,"target":53},{"source":52,"target":53},{"source":46,"target":53},{"source":53,"target":54},{"source":43,"target":55},{"source":39,"target":55},{"source":55,"target":56},{"source":26,"target":57},{"source":52,"target":57},{"source":56,"target":58},{"source":14,"target":58},{"source":59,"target":58},{"source":58,"target":60},{"source":37,"target":61},{"source":60,"target":61},{"source":39,"target":61},{"source":61,"target":62},{"source":17,"target":63},{"source":60,"target":63},{"source":34,"target":64},{"source":43,"target":64},{"source":62,"target":64},{"source":54,"target":64},{"source":60,"target":64},{"source":15,"target":64}],"scale":0.5332125839901604,"translate":[373.3250529749264,143.7733216449567]} 2 | -------------------------------------------------------------------------------- /public/reactiveFlowGraph/forceDirectedGraph.js: -------------------------------------------------------------------------------- 1 | // A force directed graph visualization module. 2 | define(["d3", "model", "lodash"], function (d3, Model, _) { 3 | 4 | // The constructor function, accepting default values. 5 | return function ForceDirectedGraph(defaults) { 6 | 7 | // Create a Model. 8 | // This will serve as the public API for the visualization. 9 | var model = Model({ 10 | 11 | // Force directed layout parameters. 12 | charge: -200, 13 | linkDistance: 140, 14 | gravity: 0.03, 15 | 16 | // The color scale. 17 | color: d3.scale.ordinal() 18 | .domain(["property", "lambda"]) 19 | .range(["#FFD1B5", "white"]) 20 | }), 21 | force = d3.layout.force(), 22 | zoom = d3.behavior.zoom(), 23 | 24 | // The size of nodes and arrows 25 | nodeSize = 20, 26 | arrowWidth = 8; 27 | 28 | // Respond to zoom interactions. 29 | zoom.on("zoom", function (){ 30 | model.scale = zoom.scale(); 31 | model.translate = zoom.translate(); 32 | }); 33 | 34 | // Call onTick each frame of the force directed layout. 35 | force.on("tick", function(e) { onTick(e); }) 36 | 37 | // This function gets reassigned later, each time new data loads. 38 | function onTick(){} 39 | 40 | // Stop propagation of drag events here so that both dragging nodes and panning are possible. 41 | // Draws from http://stackoverflow.com/questions/17953106/why-does-d3-js-v3-break-my-force-graph-when-implementing-zooming-when-v2-doesnt/17976205#17976205 42 | force.drag().on("dragstart", function () { 43 | d3.event.sourceEvent.stopPropagation(); 44 | }); 45 | 46 | // Fix node positions after the first time the user clicks and drags a node. 47 | force.drag().on("dragend", function (d) { 48 | 49 | // Stop the dragged node from moving. 50 | d.fixed = true; 51 | 52 | // Communicate this change to the outside world. 53 | serializeState(); 54 | }); 55 | 56 | 57 | // Create the SVG element from the container DOM element. 58 | model.when("container", function (container) { 59 | model.svg = d3.select(container).append("svg").call(zoom); 60 | }); 61 | 62 | // Adjust the size of the SVG based on the `box` property. 63 | model.when(["svg", "box"], function (svg, box) { 64 | svg.attr("width", box.width).attr("height", box.height); 65 | force.size([box.width, box.height]); 66 | }); 67 | 68 | // Create the SVG group that will contain the visualization. 69 | model.when("svg", function (svg) { 70 | model.g = svg.append("g"); 71 | 72 | // Arrowhead setup. 73 | // Draws from Mobile Patent Suits example: 74 | // http://bl.ocks.org/mbostock/1153292 75 | svg.append("defs") 76 | .append("marker") 77 | .attr("id", "arrow") 78 | .attr("orient", "auto") 79 | .attr("preserveAspectRatio", "none") 80 | // See also http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute 81 | //.attr("viewBox", "0 -" + arrowWidth + " 10 " + (2 * arrowWidth)) 82 | .attr("viewBox", "0 -5 10 10") 83 | // See also http://www.w3.org/TR/SVG/painting.html#MarkerElementRefXAttribute 84 | .attr("refX", 10) 85 | .attr("refY", 0) 86 | .attr("markerWidth", 10) 87 | .attr("markerHeight", arrowWidth) 88 | .append("path") 89 | .attr("d", "M0,-5L10,0L0,5"); 90 | }); 91 | 92 | // These 3 groups exist for control of Z-ordering. 93 | model.when("g", function (g) { 94 | model.nodeG = g.append("g"); 95 | model.linkG = g.append("g"); 96 | model.arrowG = g.append("g"); 97 | }); 98 | 99 | // Update the force layout with configured properties. 100 | model.when(["charge"], force.charge, force); 101 | model.when(["linkDistance"], force.linkDistance, force); 102 | model.when(["gravity"], force.gravity, force); 103 | 104 | // Update zoom scale and translation. 105 | model.when(["scale", "translate", "g"], function (scale, translate, g) { 106 | 107 | // In the case the scale and translate were set externally, 108 | if(zoom.scale() !== scale){ 109 | 110 | // update the internal D3 zoom state. 111 | zoom.scale(scale); 112 | zoom.translate(translate); 113 | } 114 | 115 | // Transform the SVG group. 116 | g.attr("transform", "translate(" + translate + ")scale(" + scale + ")"); 117 | }); 118 | 119 | // "state" represents the serialized state of the graph. 120 | model.when("state", function(state){ 121 | 122 | // Extract the scale and translate. 123 | if(state.scale && model.scale !== state.scale){ 124 | model.scale = state.scale; 125 | } 126 | if(state.translate && model.translate !== state.translate){ 127 | model.translate = state.translate; 128 | } 129 | 130 | // Set the node and link data. 131 | var newData = _.cloneDeep(state); 132 | force.nodes(newData.nodes).links(newData.links).start(); 133 | model.data = newData; 134 | }); 135 | 136 | // Update the serialized state. 137 | model.when(["scale", "translate"], _.throttle(function(scale, translate){ 138 | serializeState(); 139 | }, 1000)); 140 | 141 | // Sets model.state to expose the serialized state. 142 | function serializeState(){ 143 | var data = model.data, 144 | scale = model.scale, 145 | translate = model.translate; 146 | model.state = { 147 | nodes: data.nodes.map(function(node){ 148 | return { 149 | type: node.type, 150 | property: node.property, 151 | fixed: node.fixed, 152 | 153 | // Keep size of JSON small, so it fits in a URL. 154 | x: Math.round(node.x), 155 | y: Math.round(node.y) 156 | }; 157 | }), 158 | links: data.links.map(function(link){ 159 | // Replaced link object references with indices for serialization. 160 | return { 161 | source: link.source.index, 162 | target: link.target.index 163 | }; 164 | }), 165 | scale: scale, 166 | translate: translate 167 | }; 168 | } 169 | 170 | model.when(["data", "color", "nodeG", "linkG", "arrowG"], 171 | function(data, color, nodeG, linkG, arrowG){ 172 | var node = nodeG.selectAll("g").data(data.nodes), 173 | nodeEnter = node.enter().append("g").call(force.drag); 174 | 175 | nodeEnter.append("rect").attr("class", "node") 176 | .attr("y", -nodeSize) 177 | .attr("height", nodeSize * 2) 178 | .attr("rx", nodeSize) 179 | .attr("ry", nodeSize); 180 | 181 | nodeEnter.append("text").attr("class", "nodeLabel"); 182 | 183 | node.select("g text") 184 | 185 | // Use the property name for property nodes, and λ for lambda nodes. 186 | .text(function(d) { 187 | return (d.type === "property" ? d.property : "λ"); 188 | }) 189 | 190 | //Center text vertically. 191 | .attr("dy", function(d) { 192 | if(d.type === "lambda"){ 193 | return "0.35em"; 194 | } else { 195 | return "0.3em"; 196 | } 197 | }) 198 | 199 | // Compute rectancle sizes based on text labels. 200 | .each(function (d) { 201 | var circleWidth = nodeSize * 2, 202 | textLength = this.getComputedTextLength(), 203 | textWidth = textLength + nodeSize; 204 | 205 | if(circleWidth > textWidth) { 206 | d.isCircle = true; 207 | d.rectX = -nodeSize; 208 | d.rectWidth = circleWidth; 209 | } else { 210 | d.isCircle = false; 211 | d.rectX = -(textLength + nodeSize) / 2; 212 | d.rectWidth = textWidth; 213 | d.textLength = textLength; 214 | } 215 | }); 216 | 217 | node.select("g rect") 218 | .attr("x", function(d) { return d.rectX; }) 219 | .style("foo", function(d) { return "test"; }) 220 | .attr("width", function(d) { return d.rectWidth; }) 221 | .style("fill", function(d) { return color(d.type); }); 222 | node.exit().remove(); 223 | 224 | var link = linkG.selectAll(".link").data(data.links); 225 | link.enter().append("line").attr("class", "link") 226 | link.exit().remove(); 227 | 228 | var arrow = arrowG.selectAll(".arrow").data(data.links); 229 | arrow.enter().append("line") 230 | .attr("class", "arrow") 231 | .attr("marker-end", function(d) { return "url(#arrow)" }); 232 | arrow.exit().remove(); 233 | 234 | // Run a modified version of force directed layout 235 | // to account for link direction going from left to right. 236 | onTick = function(e) { 237 | 238 | // Execute left-right constraints 239 | var k = 1 * e.alpha; 240 | force.links().forEach(function (link) { 241 | var a = link.source, 242 | b = link.target, 243 | dx = b.x - a.x, 244 | dy = b.y - a.y, 245 | d = Math.sqrt(dx * dx + dy * dy), 246 | x = (a.x + b.x) / 2; 247 | if(!a.fixed){ 248 | a.x += k * (x - d / 2 - a.x); 249 | } 250 | if(!b.fixed){ 251 | b.x += k * (x + d / 2 - b.x); 252 | } 253 | }); 254 | force.nodes().forEach(function (d) { 255 | if(d.isCircle){ 256 | d.leftX = d.rightX = d.x; 257 | } else { 258 | d.leftX = d.x - d.textLength / 2 + nodeSize / 2; 259 | d.rightX = d.x + d.textLength / 2 - nodeSize / 2; 260 | } 261 | }); 262 | 263 | link.call(edge); 264 | arrow.call(edge); 265 | 266 | node.attr("transform", function(d) { 267 | return "translate(" + d.x + "," + d.y + ")"; 268 | }); 269 | }; 270 | }); 271 | 272 | // Sets the (x1, y1, x2, y2) line properties for graph edges. 273 | function edge(selection){ 274 | selection 275 | .each(function (d) { 276 | var sourceX, targetX, dy, dy, angle; 277 | 278 | if( d.source.rightX < d.target.leftX ){ 279 | sourceX = d.source.rightX; 280 | targetX = d.target.leftX; 281 | } else if( d.target.rightX < d.source.leftX ){ 282 | targetX = d.target.rightX; 283 | sourceX = d.source.leftX; 284 | } else if (d.target.isCircle) { 285 | targetX = sourceX = d.target.x; 286 | } else if (d.source.isCircle) { 287 | targetX = sourceX = d.source.x; 288 | } else { 289 | targetX = sourceX = (d.source.x + d.target.x) / 2; 290 | } 291 | 292 | dx = targetX - sourceX; 293 | dy = d.target.y - d.source.y; 294 | angle = Math.atan2(dx, dy); 295 | 296 | d.sourceX = sourceX + Math.sin(angle) * nodeSize; 297 | d.targetX = targetX - Math.sin(angle) * nodeSize; 298 | d.sourceY = d.source.y + Math.cos(angle) * nodeSize; 299 | d.targetY = d.target.y - Math.cos(angle) * nodeSize; 300 | }) 301 | .attr("x1", function(d) { return d.sourceX; }) 302 | .attr("y1", function(d) { return d.sourceY; }) 303 | .attr("x2", function(d) { return d.targetX; }) 304 | .attr("y2", function(d) { return d.targetY; }); 305 | } 306 | 307 | model.set(defaults); 308 | 309 | return model; 310 | }; 311 | }); 312 | -------------------------------------------------------------------------------- /public/reactiveFlowGraph/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 19 | 20 | 21 | 22 | 23 | Data Flow Diagram 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /public/reactiveFlowGraph/main.js: -------------------------------------------------------------------------------- 1 | require(["d3", "forceDirectedGraph", "lodash"], function (d3, ForceDirectedGraph, lodash) { 2 | 3 | // Initialize the force directed graph. 4 | var container = d3.select("#container").node(), 5 | forceDirectedGraph = ForceDirectedGraph({ container: container }); 6 | 7 | // Initialize zoom based on client size. 8 | var scale = container.clientWidth * 1 / 800; 9 | forceDirectedGraph.scale = scale; 10 | forceDirectedGraph.translate = [ 11 | container.clientWidth / 2 * (1 - scale), 12 | container.clientHeight / 2 * (1 - scale) 13 | ]; 14 | 15 | 16 | // Set up default data. 17 | if(!location.hash){ 18 | location.hash = '{"nodes":[{"type":"lambda","fixed":0,"x":442,"y":250},{"type":"property","property":"firstName","fixed":1,"x":290,"y":212},{"type":"property","property":"lastName","fixed":1,"x":293,"y":294},{"type":"property","property":"fullName","fixed":0,"x":581,"y":247}],"links":[{"source":1,"target":0},{"source":2,"target":0},{"source":0,"target":3}],"scale":1.938287710980903,"translate":[-360.71751731834274,-241.583180104211]}'; 19 | } 20 | 21 | // Update the fragment identifier in response to user interactions. 22 | forceDirectedGraph.when(["state"], function(state){ 23 | location.hash = JSON.stringify(state); 24 | console.log(JSON.stringify(state)); 25 | }); 26 | 27 | // Sets the data on the graph visualization from the fragment identifier. 28 | // See https://github.com/curran/screencasts/blob/gh-pages/navigation/examples/code/snapshot11/main.js 29 | function navigate(){ 30 | if(location.hash){ 31 | var newState = JSON.parse(location.hash.substr(1)); 32 | if(JSON.stringify(newState) !== JSON.stringify(forceDirectedGraph.state)){ 33 | forceDirectedGraph.state = newState; 34 | } 35 | } 36 | } 37 | 38 | // Navigate once to the initial hash value. 39 | navigate(); 40 | 41 | // Navigate whenever the fragment identifier value changes. 42 | window.addEventListener("hashchange", navigate); 43 | 44 | // Sets the `box` model property 45 | // based on the size of the container, 46 | function computeBox(){ 47 | forceDirectedGraph.box = { 48 | width: container.clientWidth, 49 | height: container.clientHeight 50 | }; 51 | } 52 | 53 | // once to initialize `model.box`, and 54 | computeBox(); 55 | 56 | // whenever the browser window resizes in the future. 57 | window.addEventListener("resize", computeBox); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /public/reactiveFlowGraph/model.js: -------------------------------------------------------------------------------- 1 | // Implements key-value models with a functional reactive `when` operator. 2 | // See also https://github.com/curran/model 3 | define([], function (){ 4 | 5 | // The constructor function, accepting default values. 6 | return function Model(defaults){ 7 | 8 | // The returned public API object. 9 | var model = {}, 10 | 11 | // The internal stored values for tracked properties. { property -> value } 12 | values = {}, 13 | 14 | // The listeners for each tracked property. { property -> [callback] } 15 | listeners = {}, 16 | 17 | // The set of tracked properties. { property -> true } 18 | trackedProperties = {}; 19 | 20 | // The functional reactive "when" operator. 21 | // 22 | // * `properties` An array of property names (can also be a single property string). 23 | // * `callback` A callback function that is called: 24 | // * with property values as arguments, ordered corresponding to the properties array, 25 | // * only if all specified properties have values, 26 | // * once for initialization, 27 | // * whenever one or more specified properties change, 28 | // * on the next tick of the JavaScript event loop after properties change, 29 | // * only once as a result of one or more synchronous changes to dependency properties. 30 | function when(properties, callback){ 31 | 32 | // This function will trigger the callback to be invoked. 33 | var triggerCallback = debounce(function (){ 34 | var args = properties.map(function(property){ 35 | return values[property]; 36 | }); 37 | if(allAreDefined(args)){ 38 | callback.apply(null, args); 39 | } 40 | }); 41 | 42 | // Handle either an array or a single string. 43 | properties = (properties instanceof Array) ? properties : [properties]; 44 | 45 | // Trigger the callback once for initialization. 46 | triggerCallback(); 47 | 48 | // Trigger the callback whenever specified properties change. 49 | properties.forEach(function(property){ 50 | on(property, triggerCallback); 51 | }); 52 | } 53 | 54 | // Returns a debounced version of the given function. 55 | // See http://underscorejs.org/#debounce 56 | function debounce(callback){ 57 | var queued = false; 58 | return function () { 59 | if(!queued){ 60 | queued = true; 61 | setTimeout(function () { 62 | queued = false; 63 | callback(); 64 | }, 0); 65 | } 66 | }; 67 | } 68 | 69 | // Returns true if all elements of the given array are defined, false otherwise. 70 | function allAreDefined(arr){ 71 | return !arr.some(function (d) { 72 | return typeof d === 'undefined' || d === null; 73 | }); 74 | } 75 | 76 | // Adds a change listener for a given property with Backbone-like behavior. 77 | // See http://backbonejs.org/#Events-on 78 | function on(property, callback){ 79 | getListeners(property).push(callback); 80 | track(property); 81 | }; 82 | 83 | // Gets or creates the array of listener functions for a given property. 84 | function getListeners(property){ 85 | return listeners[property] || (listeners[property] = []); 86 | } 87 | 88 | // Tracks a property if it is not already tracked. 89 | function track(property){ 90 | if(!(property in trackedProperties)){ 91 | trackedProperties[property] = true; 92 | values[property] = model[property]; 93 | Object.defineProperty(model, property, { 94 | get: function () { return values[property]; }, 95 | set: function(value) { 96 | values[property] = value; 97 | getListeners(property).forEach(function(callback){ 98 | callback(value); 99 | }); 100 | } 101 | }); 102 | } 103 | } 104 | 105 | // Sets all of the given values on the model. 106 | // Values is an object { property -> value }. 107 | function set(values){ 108 | for(property in values){ 109 | model[property] = values[property]; 110 | } 111 | } 112 | 113 | // Transfer defaults passed into the constructor to the model. 114 | set(defaults); 115 | 116 | // Expose the public API. 117 | model.when = when; 118 | model.on = on; 119 | model.set = set 120 | return model; 121 | } 122 | }); 123 | -------------------------------------------------------------------------------- /public/reactiveFlowGraph/styles.css: -------------------------------------------------------------------------------- 1 | /* Make the visualization container fill the page. */ 2 | #container { 3 | position: fixed; 4 | left: 0px; 5 | right: 0px; 6 | top: 0px; 7 | bottom: 0px; 8 | } 9 | 10 | /* Style the nodes of the graph. */ 11 | .node { 12 | stroke: black; 13 | stroke-width: 1.5; 14 | } 15 | .nodeLabel { 16 | font-size: 2em; 17 | /* Center text horizontally */ 18 | text-anchor: middle; 19 | } 20 | 21 | /* Style the links of the graph. */ 22 | .link { 23 | stroke: black; 24 | } 25 | 26 | /* Set the arrowhead size. */ 27 | .arrow { 28 | stroke-width: 1.5px; 29 | } 30 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var app = express(); 3 | 4 | app.use(express.static("public")); 5 | 6 | // Writes the metadata for the graph with the given name. 7 | app.post("/write/:graphName", function (req, res) { 8 | 9 | // Write the data to public/graphs/{{graphName}}-meta.json 10 | // * pan 11 | // * zoom 12 | // * fixedNodes: id -> 13 | // * x: int 14 | // * y: int 15 | 16 | res.send("Success!"); 17 | }); 18 | 19 | app.listen(3000, function () { 20 | console.log("Example app listening on port 3000!"); 21 | }); 22 | --------------------------------------------------------------------------------