diff --git a/LesionTracker/.meteor/.finished-upgraders b/LesionTracker/.meteor/.finished-upgraders index a541808f2..2a56593d3 100644 --- a/LesionTracker/.meteor/.finished-upgraders +++ b/LesionTracker/.meteor/.finished-upgraders @@ -14,3 +14,5 @@ notices-for-facebook-graph-api-2 1.3.5-remove-old-dev-bundle-link 1.4.0-remove-old-dev-bundle-link 1.4.1-add-shell-server-package +1.4.3-split-account-service-packages +1.5-add-dynamic-import-package diff --git a/LesionTracker/.meteor/packages b/LesionTracker/.meteor/packages index 4892d07cf..9acfd07f2 100644 --- a/LesionTracker/.meteor/packages +++ b/LesionTracker/.meteor/packages @@ -6,16 +6,25 @@ npm-bcrypt@0.9.2 -meteor-base@1.0.4 # Packages every Meteor app needs to have +meteor-base@1.1.0 # Packages every Meteor app needs to have mobile-experience@1.0.4 # Packages for a great mobile UX -mongo@1.1.14 # The database Meteor supports right now +mongo@1.1.18 # The database Meteor supports right now blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views session@1.1.7 # Client-side reactive dictionary for your app jquery@1.11.10 # Helpful client-side library -tracker@1.1.1 # Meteor's client-side reactive programming library - +tracker@1.1.3 # Meteor's client-side reactive programming library +standard-minifier-css +standard-minifier-js +http@1.2.12 +promise@0.8.9 +stylus@2.513.9 +random@1.0.10 +reactive-var@1.0.11 +reactive-dict@1.1.9 +check@1.2.5 +email@1.2.1 es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers. -ecmascript@0.6.1 # Enable ECMAScript2015+ syntax in app code +ecmascript@0.8.0 # Enable ECMAScript2015+ syntax in app code clinical:active-entry clinical:theming @@ -23,50 +32,44 @@ clinical:fonts clinical:hipaa-audit-log clinical:hipaa-logger -aldeed:simple-schema # Third party package to deal with schemas - # OHIF Packages ohif:polyfill ohif:design ohif:core -ohif:user ohif:header -ohif:select-tree ohif:cornerstone ohif:viewerbase ohif:study-list ohif:dicom-services ohif:hanging-protocols -ohif:user-management -ohif:lesiontracker ohif:metadata -accounts-base@1.2.14 -accounts-password@1.3.3 -ian:accounts-ui-bootstrap-3 -twbs:bootstrap -http@1.2.10 -arsnebula:reactive-promise -promise@0.8.8 -stylus@2.513.8 -fortawesome:fontawesome -random@1.0.10 -reactive-var@1.0.11 -reactive-dict@1.1.8 -practicalmeteor:loglevel -momentjs:moment@2.15.1 +# Necessary for Lesion Trakcer +ohif:lesiontracker +ohif:user +ohif:user-management +ohif:select-tree -anti:gagarin@=0.4.11 -check@1.2.4 +accounts-base@1.3.0 +accounts-password@1.3.6 + +twbs:bootstrap +fortawesome:fontawesome +momentjs:moment@2.15.1 +aldeed:simple-schema # Third party package to deal with schemas aldeed:template-extension@4.0.0 +aldeed:collection2 zuuk:stale-session +<<<<<<< 3d081fe297551d37cd9691a7c60be1f743b9e600 email@1.1.18 peppelg:bootstrap-3-modal simple:reactive-method fastclick@1.0.13 standard-minifier-css@1.3.2 standard-minifier-js@1.2.1 +======= +gilbertwat:bootstrap3-daterangepicker +>>>>>>> Move towards NPM for Cornerstone dependencies johdirr:meteor-git-rev u2622:persistent-session@0.4.4 -shell-server@0.2.1 -aldeed:collection2 +shell-server@0.2.3 \ No newline at end of file diff --git a/LesionTracker/.meteor/release b/LesionTracker/.meteor/release index 61f6c675b..025f64e70 100644 --- a/LesionTracker/.meteor/release +++ b/LesionTracker/.meteor/release @@ -1 +1 @@ -METEOR@1.4.2.3 +METEOR@1.5 diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions index 7bbba2028..d9284eeed 100644 --- a/LesionTracker/.meteor/versions +++ b/LesionTracker/.meteor/versions @@ -1,5 +1,5 @@ -accounts-base@1.2.14 -accounts-password@1.3.3 +accounts-base@1.3.0 +accounts-password@1.3.6 aldeed:collection2@2.10.0 aldeed:collection2-core@1.2.0 aldeed:schema-deny@1.1.0 @@ -8,41 +8,44 @@ aldeed:simple-schema@1.5.3 aldeed:template-extension@4.0.0 allow-deny@1.0.5 amplify@1.0.0 -anti:gagarin@0.4.11 -anti:i18n@0.4.3 -arsnebula:reactive-promise@0.9.1 autoupdate@1.3.12 -babel-compiler@6.13.0 +babel-compiler@6.19.1 babel-runtime@1.0.1 base64@1.0.10 binary-heap@1.0.10 -blaze@2.2.0 +blaze@2.3.2 blaze-html-templates@1.0.5 blaze-tools@1.0.10 -boilerplate-generator@1.0.11 +boilerplate-generator@1.1.0 caching-compiler@1.1.9 caching-html-compiler@1.0.7 callback-hook@1.0.10 -check@1.2.4 +check@1.2.5 clinical:active-entry@1.5.16 clinical:auto-resizing@0.2.0 clinical:fonts@1.1.4 clinical:hipaa-audit-log@2.4.2 clinical:hipaa-logger@1.0.1 clinical:theming@0.4.10 +<<<<<<< 3d081fe297551d37cd9691a7c60be1f743b9e600 coffeescript@1.11.1_4 dangrossman:bootstrap-daterangepicker@2.1.13 +======= +>>>>>>> Move towards NPM for Cornerstone dependencies ddp@1.2.5 -ddp-client@1.3.2 +ddp-client@1.3.4 ddp-common@1.2.8 -ddp-rate-limiter@1.0.6 -ddp-server@1.3.12 +ddp-rate-limiter@1.0.7 +ddp-server@1.3.14 deps@1.0.12 diff-sequence@1.0.7 -ecmascript@0.6.1 -ecmascript-runtime@0.3.15 +dynamic-import@0.1.0 +ecmascript@0.8.0 +ecmascript-runtime@0.4.1 +ecmascript-runtime-client@0.4.1 +ecmascript-runtime-server@0.4.1 ejson@1.0.13 -email@1.1.18 +email@1.2.1 es5-shim@4.6.15 fastclick@1.0.13 fortawesome:fontawesome@4.7.0 @@ -51,8 +54,7 @@ grove:less@0.2.0 hot-code-push@1.0.4 html-tools@1.0.11 htmljs@1.0.11 -http@1.2.10 -ian:accounts-ui-bootstrap-3@1.2.89 +http@1.2.12 id-map@1.0.9 iron:controller@1.0.12 iron:core@1.0.11 @@ -64,30 +66,35 @@ iron:router@1.0.13 iron:url@1.0.11 johdirr:meteor-git-rev@0.0.4 jquery@1.11.10 -launch-screen@1.1.0 +launch-screen@1.1.1 livedata@1.0.18 -localstorage@1.0.12 -logging@1.1.16 +localstorage@1.1.0 +logging@1.1.17 mdg:validation-error@0.5.1 -meteor@1.6.0 -meteor-base@1.0.4 +meteor@1.6.1 +meteor-base@1.1.0 meteor-platform@1.2.6 -minifier-css@1.2.15 -minifier-js@1.2.15 -minimongo@1.0.19 +minifier-css@1.2.16 +minifier-js@2.1.0 +minimongo@1.2.0 mobile-experience@1.0.4 -mobile-status-bar@1.0.13 -modules@0.7.7 -modules-runtime@0.7.7 +mobile-status-bar@1.0.14 +modules@0.9.0 +modules-runtime@0.8.0 momentjs:moment@2.16.0 -mongo@1.1.14 +mongo@1.1.18 mongo-id@1.0.6 mrt:moment@2.8.1 natestrauser:select2@4.0.3 npm-bcrypt@0.9.2 +<<<<<<< 3d081fe297551d37cd9691a7c60be1f743b9e600 npm-mongo@2.2.11_2 observe-sequence@1.0.14 ohif:commands@0.0.1 +======= +npm-mongo@2.2.24 +observe-sequence@1.0.16 +>>>>>>> Move towards NPM for Cornerstone dependencies ohif:core@0.0.1 ohif:cornerstone@0.0.1 ohif:design@0.0.1 @@ -109,14 +116,11 @@ ohif:user-management@0.0.1 ohif:viewerbase@0.0.1 ohif:wadoproxy@0.0.1 ordered-dict@1.0.9 -peppelg:bootstrap-3-modal@1.0.4 -practicalmeteor:chai@2.1.0_1 -practicalmeteor:loglevel@1.2.0_2 -promise@0.8.8 +promise@0.8.9 raix:eventemitter@0.1.3 random@1.0.10 -rate-limit@1.0.6 -reactive-dict@1.1.8 +rate-limit@1.0.8 +reactive-dict@1.1.9 reactive-var@1.0.11 reload@1.1.11 retry@1.0.9 @@ -124,29 +128,28 @@ routepolicy@1.0.12 service-configuration@1.0.11 session@1.1.7 sha@1.0.9 -shell-server@0.2.1 +shell-server@0.2.3 silentcicero:jszip@0.0.4 -simple:reactive-method@1.0.2 -spacebars@1.0.13 -spacebars-compiler@1.0.13 +spacebars@1.0.15 +spacebars-compiler@1.1.2 srp@1.0.10 standard-app-packages@1.0.9 -standard-minifier-css@1.3.2 -standard-minifier-js@1.2.1 -stylus@2.513.8 +standard-minifier-css@1.3.4 +standard-minifier-js@2.1.0 +stylus@2.513.9 templating@1.2.15 templating-compiler@1.2.15 templating-runtime@1.2.15 -templating-tools@1.0.5 -tracker@1.1.1 +templating-tools@1.1.1 +tracker@1.1.3 twbs:bootstrap@3.3.6 typ:accounts-ldap@1.0.1 typ:ldapjs@0.7.3 u2622:persistent-session@0.4.4 -ui@1.0.12 +ui@1.0.13 underscore@1.0.10 -url@1.0.11 +url@1.1.0 validatejs@0.0.1 -webapp@1.3.12 +webapp@1.3.16 webapp-hashing@1.0.9 zuuk:stale-session@1.0.8 diff --git a/LesionTracker/client/log.js b/LesionTracker/client/log.js index f4e50c3e4..11681dffc 100644 --- a/LesionTracker/client/log.js +++ b/LesionTracker/client/log.js @@ -1,3 +1,3 @@ -// Create application logger using loglevel -// https://atmospherejs.com/spacejamio/loglevel -log = loglevel.createAppLogger('LesionTracker', defaultLevel = 'info'); +import loglevel from 'loglevel'; +log = loglevel.getLogger('OHIFViewer'); +log.setLevel('info'); \ No newline at end of file diff --git a/LesionTracker/client/routes.js b/LesionTracker/client/routes.js index 098ae15dc..1993f5413 100644 --- a/LesionTracker/client/routes.js +++ b/LesionTracker/client/routes.js @@ -30,15 +30,21 @@ Router.route('/', function() { Router.go('studylist', {}, { replaceState: true }); }, { name: 'home' }); -Router.route('/studylist', function() { - // Retrieve the timepoints data to display in studylist - const promise = OHIF.studylist.timepointApi.retrieveTimepoints({}).then(() => { - this.render('app', { data: { template: 'studylist' } }); - }); +Router.route('/studylist', { + name: 'studylist', + onBeforeAction: function() { + const next = this.next; - // Show loading state while preparing the studylist data - OHIF.ui.showDialog('dialogLoading', { promise }); -}, { name: 'studylist' }); + // Retrieve the timepoints data to display in studylist + const promise = OHIF.studylist.timepointApi.retrieveTimepoints({}); + promise.then(() => { + next() + }); + }, + render: function() { + this.render('app', { data: { template: 'studylist' } }); + } +}); Router.route('/viewer/timepoints/:timepointId', function() { const timepointId = this.params.timepointId; diff --git a/LesionTracker/package.json b/LesionTracker/package.json index 2a12ae603..d5f8eb438 100644 --- a/LesionTracker/package.json +++ b/LesionTracker/package.json @@ -11,6 +11,7 @@ "dependencies": { "babel-runtime": "^6.20.0", "bcrypt": "^0.8.7", + "loglevel": "^1.4.1", "meteor-node-stubs": "^0.2.3" }, "devDependencies": { diff --git a/LesionTracker/server/dropAllCollections.js b/LesionTracker/server/dropAllCollections.js deleted file mode 100644 index e6420782a..000000000 --- a/LesionTracker/server/dropAllCollections.js +++ /dev/null @@ -1,16 +0,0 @@ -// Temporary fix to drop all Collections on server restart -// http://stackoverflow.com/questions/23891631/meteor-how-can-i-drop-all-mongo-collections-and-clear-all-data-on-startup -Meteor.startup(() => { - if (Meteor.settings.dropCollections === true) { - console.warn('Dropping all global Collections!'); - Object.keys(global).forEach((key) => { - const object = global[key]; - if (object instanceof Meteor.Collection) { - if (!(/^server|currentServer$/).test(object._name)) { - console.warn(`Dropping: ${object._debugName || object._name}`); - object.remove({}); - } - } - }); - } -}); diff --git a/OHIFViewer/.meteor/.finished-upgraders b/OHIFViewer/.meteor/.finished-upgraders index a541808f2..2a56593d3 100644 --- a/OHIFViewer/.meteor/.finished-upgraders +++ b/OHIFViewer/.meteor/.finished-upgraders @@ -14,3 +14,5 @@ notices-for-facebook-graph-api-2 1.3.5-remove-old-dev-bundle-link 1.4.0-remove-old-dev-bundle-link 1.4.1-add-shell-server-package +1.4.3-split-account-service-packages +1.5-add-dynamic-import-package diff --git a/OHIFViewer/.meteor/packages b/OHIFViewer/.meteor/packages index d4d38fe64..a5948e68b 100644 --- a/OHIFViewer/.meteor/packages +++ b/OHIFViewer/.meteor/packages @@ -4,6 +4,30 @@ # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. +http@1.2.12 +promise@0.8.9 +stylus@2.513.9 +meteor-base@1.1.0 +mobile-experience@1.0.4 +mongo@1.1.18 +blaze-html-templates@1.0.4 +session@1.1.7 +jquery@1.11.10 +tracker@1.1.3 +logging@1.1.17 +reload@1.1.11 +random@1.0.10 +ejson@1.0.13 +spacebars@1.0.12 +check@1.2.5 +ecmascript@0.8.0 +es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers. +reactive-var@1.0.11 +reactive-dict@1.1.9 +standard-minifier-css@1.3.4 +standard-minifier-js@2.1.0 + + # OHIF Packages ohif:polyfill ohif:design @@ -16,39 +40,14 @@ ohif:viewerbase ohif:study-list ohif:dicom-services ohif:hanging-protocols -# ohif:user-management ohif:metadata twbs:bootstrap -http@1.2.10 -arsnebula:reactive-promise -promise@0.8.8 -stylus@2.513.8 fortawesome:fontawesome -meteor-base@1.0.4 -mobile-experience@1.0.4 -mongo@1.1.14 -blaze-html-templates@1.0.4 -session@1.1.7 -jquery@1.11.10 -tracker@1.1.1 -logging@1.1.16 -reload@1.1.11 -random@1.0.10 -ejson@1.0.13 -spacebars@1.0.12 -check@1.2.4 +momentjs:moment@2.15.1 aldeed:simple-schema # Third party package to deal with schemas -ecmascript@0.6.1 -reactive-var@1.0.11 - -practicalmeteor:loglevel -reactive-dict@1.1.8 -fastclick@1.0.13 -standard-minifier-css@1.3.2 -standard-minifier-js@1.2.1 +aldeed:template-extension +gilbertwat:bootstrap3-daterangepicker johdirr:meteor-git-rev u2622:persistent-session@0.4.4 -aldeed:template-extension -peppelg:bootstrap-3-modal -shell-server@0.2.1 +shell-server@0.2.3 diff --git a/OHIFViewer/.meteor/release b/OHIFViewer/.meteor/release index 61f6c675b..025f64e70 100644 --- a/OHIFViewer/.meteor/release +++ b/OHIFViewer/.meteor/release @@ -1 +1 @@ -METEOR@1.4.2.3 +METEOR@1.5 diff --git a/OHIFViewer/.meteor/versions b/OHIFViewer/.meteor/versions index 36aaba3f1..a59816e60 100644 --- a/OHIFViewer/.meteor/versions +++ b/OHIFViewer/.meteor/versions @@ -1,45 +1,52 @@ -accounts-base@1.2.14 +accounts-base@1.3.0 aldeed:collection2@2.10.0 aldeed:collection2-core@1.2.0 aldeed:schema-deny@1.1.0 aldeed:schema-index@1.1.1 aldeed:simple-schema@1.5.3 -aldeed:template-extension@4.0.0 +aldeed:template-extension@4.1.0 allow-deny@1.0.5 amplify@1.0.0 -arsnebula:reactive-promise@0.9.1 autoupdate@1.3.12 -babel-compiler@6.13.0 +babel-compiler@6.19.2 babel-runtime@1.0.1 base64@1.0.10 binary-heap@1.0.10 -blaze@2.2.0 -blaze-html-templates@1.0.5 +blaze@2.3.2 +blaze-html-templates@1.1.2 blaze-tools@1.0.10 -boilerplate-generator@1.0.11 +boilerplate-generator@1.1.0 caching-compiler@1.1.9 -caching-html-compiler@1.0.7 +caching-html-compiler@1.1.2 callback-hook@1.0.10 +<<<<<<< 3d081fe297551d37cd9691a7c60be1f743b9e600 check@1.2.4 coffeescript@1.11.1_4 dangrossman:bootstrap-daterangepicker@2.1.13 +======= +check@1.2.5 +>>>>>>> Move towards NPM for Cornerstone dependencies ddp@1.2.5 -ddp-client@1.3.2 +ddp-client@1.3.4 ddp-common@1.2.8 -ddp-rate-limiter@1.0.6 -ddp-server@1.3.12 +ddp-rate-limiter@1.0.7 +ddp-server@1.3.14 deps@1.0.12 diff-sequence@1.0.7 -ecmascript@0.6.1 -ecmascript-runtime@0.3.15 +dynamic-import@0.1.1 +ecmascript@0.8.0 +ecmascript-runtime@0.4.1 +ecmascript-runtime-client@0.4.2 +ecmascript-runtime-server@0.4.1 ejson@1.0.13 +es5-shim@4.6.15 fastclick@1.0.13 fortawesome:fontawesome@4.7.0 geojson-utils@1.0.10 hot-code-push@1.0.4 html-tools@1.0.11 htmljs@1.0.11 -http@1.2.10 +http@1.2.12 id-map@1.0.9 iron:controller@1.0.12 iron:core@1.0.11 @@ -51,28 +58,33 @@ iron:router@1.0.13 iron:url@1.0.11 johdirr:meteor-git-rev@0.0.4 jquery@1.11.10 -launch-screen@1.1.0 +launch-screen@1.1.1 livedata@1.0.18 -localstorage@1.0.12 -logging@1.1.16 +localstorage@1.1.0 +logging@1.1.17 mdg:validation-error@0.5.1 -meteor@1.6.0 -meteor-base@1.0.4 +meteor@1.6.1 +meteor-base@1.1.0 meteor-platform@1.2.6 -minifier-css@1.2.15 -minifier-js@1.2.15 -minimongo@1.0.19 +minifier-css@1.2.16 +minifier-js@2.1.0 +minimongo@1.2.1 mobile-experience@1.0.4 -mobile-status-bar@1.0.13 -modules@0.7.7 -modules-runtime@0.7.7 +mobile-status-bar@1.0.14 +modules@0.9.1 +modules-runtime@0.8.0 momentjs:moment@2.16.0 -mongo@1.1.14 +mongo@1.1.18 mongo-id@1.0.6 natestrauser:select2@4.0.3 +<<<<<<< 3d081fe297551d37cd9691a7c60be1f743b9e600 npm-mongo@2.2.11_2 observe-sequence@1.0.14 ohif:commands@0.0.1 +======= +npm-mongo@2.2.24 +observe-sequence@1.0.16 +>>>>>>> Move towards NPM for Cornerstone dependencies ohif:core@0.0.1 ohif:cornerstone@0.0.1 ohif:design@0.0.1 @@ -89,38 +101,35 @@ ohif:themes-common@0.0.1 ohif:viewerbase@0.0.1 ohif:wadoproxy@0.0.1 ordered-dict@1.0.9 -peppelg:bootstrap-3-modal@1.0.4 -practicalmeteor:chai@2.1.0_1 -practicalmeteor:loglevel@1.2.0_2 -promise@0.8.8 +promise@0.8.9 raix:eventemitter@0.1.3 random@1.0.10 -rate-limit@1.0.6 -reactive-dict@1.1.8 +rate-limit@1.0.8 +reactive-dict@1.1.9 reactive-var@1.0.11 reload@1.1.11 retry@1.0.9 routepolicy@1.0.12 service-configuration@1.0.11 session@1.1.7 -shell-server@0.2.1 +shell-server@0.2.3 silentcicero:jszip@0.0.4 -spacebars@1.0.13 -spacebars-compiler@1.0.13 +spacebars@1.0.15 +spacebars-compiler@1.1.2 standard-app-packages@1.0.9 -standard-minifier-css@1.3.2 -standard-minifier-js@1.2.1 -stylus@2.513.8 -templating@1.2.15 -templating-compiler@1.2.15 -templating-runtime@1.2.15 -templating-tools@1.0.5 -tracker@1.1.1 +standard-minifier-css@1.3.4 +standard-minifier-js@2.1.0 +stylus@2.513.9 +templating@1.3.2 +templating-compiler@1.3.2 +templating-runtime@1.3.2 +templating-tools@1.1.2 +tracker@1.1.3 twbs:bootstrap@3.3.6 u2622:persistent-session@0.4.4 -ui@1.0.12 +ui@1.0.13 underscore@1.0.10 -url@1.0.11 +url@1.1.0 validatejs@0.0.1 -webapp@1.3.12 +webapp@1.3.16 webapp-hashing@1.0.9 diff --git a/OHIFViewer/client/log.js b/OHIFViewer/client/log.js index 1f567f0c5..5f3ef93a3 100644 --- a/OHIFViewer/client/log.js +++ b/OHIFViewer/client/log.js @@ -1,3 +1,3 @@ -// Create application logger using loglevel -// https://atmospherejs.com/spacejamio/loglevel -log = loglevel.createAppLogger('OHIFViewer', defaultLevel = 'info'); +import loglevel from 'loglevel'; +log = loglevel.getLogger('OHIFViewer'); +log.setLevel('info'); diff --git a/OHIFViewer/package.json b/OHIFViewer/package.json index d0a62cfa3..1a44295e7 100644 --- a/OHIFViewer/package.json +++ b/OHIFViewer/package.json @@ -4,7 +4,8 @@ "description": "", "main": "index.js", "dependencies": { - "babel-runtime": "^6.22.0" + "babel-runtime": "^6.22.0", + "loglevel": "^1.4.1" }, "devDependencies": {}, "scripts": { diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/loading.js b/Packages/ohif-core/client/components/bootstrap/dialog/loading.js index cbcef6b85..60f1bfc83 100644 --- a/Packages/ohif-core/client/components/bootstrap/dialog/loading.js +++ b/Packages/ohif-core/client/components/bootstrap/dialog/loading.js @@ -9,6 +9,6 @@ Template.dialogLoading.onRendered(() => { $modal.modal({ backdrop: 'static', keyboard: false, - modal: true + show: true }); }); diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/simple.js b/Packages/ohif-core/client/components/bootstrap/dialog/simple.js index f44cb2cb9..91f998759 100644 --- a/Packages/ohif-core/client/components/bootstrap/dialog/simple.js +++ b/Packages/ohif-core/client/components/bootstrap/dialog/simple.js @@ -8,7 +8,7 @@ Template.dialogSimple.onCreated(() => { instance.close = () => { const $modal = instance.$('.modal'); - $modal.on('hidden.bs.modal', () => instance.data.promiseResolve()).modal('hide'); + $modal.one('hidden.bs.modal', () => instance.data.promiseResolve()).modal('hide'); }; // Automatically close the modal if a timeout value was given diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/unsavedChangesDialog.js b/Packages/ohif-core/client/components/bootstrap/dialog/unsavedChangesDialog.js index 1e42a20df..df3e1b665 100644 --- a/Packages/ohif-core/client/components/bootstrap/dialog/unsavedChangesDialog.js +++ b/Packages/ohif-core/client/components/bootstrap/dialog/unsavedChangesDialog.js @@ -12,7 +12,7 @@ Template.unsavedChangesDialog.onRendered(function() { const instance = Template.instance(); const $modal = instance.$('.modal.unsavedChangesDialog'); - // Routine which effectivelly displays the BS modal... + // Routine which effectively displays the BS modal... instance.displayModal = () => { // Make modal options extensible... @@ -22,7 +22,7 @@ Template.unsavedChangesDialog.onRendered(function() { }, instance.data.modalOptions); // Set handler for "hidden" event... Simply remove the view! - $modal.on('hidden.bs.modal', () => { + $modal.one('hidden.bs.modal', () => { Blaze.remove(instance.view); }); diff --git a/Packages/ohif-cornerstone/client/cornerstone.js b/Packages/ohif-cornerstone/client/cornerstone.js deleted file mode 100644 index 39ae790d6..000000000 --- a/Packages/ohif-cornerstone/client/cornerstone.js +++ /dev/null @@ -1,4585 +0,0 @@ -/*! cornerstone - v0.10.7 - 2017-04-14 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstone */ -if(typeof cornerstone === 'undefined'){ - cornerstone = { - internal : {}, - rendering: {}, - colors: {} - }; -} - -(function (cornerstone) { - - "use strict"; - - /** - * Converts a point in the canvas coordinate system to the pixel coordinate system - * system. This can be used to reset tools' image coordinates after modifications - * have been made in canvas space (e.g. moving a tool by a few cm, independent of - * image resolution). - * - * @param element - * @param pt - * @returns {x: number, y: number} - */ - function canvasToPixel(element, pt) { - var enabledElement = cornerstone.getEnabledElement(element); - var transform = cornerstone.internal.getTransform(enabledElement); - transform.invert(); - return transform.transformPoint(pt.x, pt.y); - } - - // module/private exports - cornerstone.canvasToPixel = canvasToPixel; - -}(cornerstone)); - -(function(cornerstone) { - - var COLOR_BLACK = [0, 0, 0, 255]; - - // Colormaps - // - // Hot Iron, PET, Hot Metal Blue and PET 20 Step are color palattes - // defined by the DICOM standard - // http://dicom.nema.org/dicom/2013/output/chtml/part06/chapter_B.html - // - // All Linear Segmented Colormaps were copied from matplotlib - // https://github.com/stefanv/matplotlib/blob/master/lib/matplotlib/_cm.py - - var colormapsData = { - hotIron: { - name: 'Hot Iron', - numOfColors: 256, - colors: [ - [0, 0, 0, 255], [2, 0, 0, 255], [4, 0, 0, 255], [6, 0, 0, 255], [8, 0, 0, 255], - [10, 0, 0, 255], [12, 0, 0, 255], [14, 0, 0, 255], [16, 0, 0, 255], [18, 0, 0, 255], - [20, 0, 0, 255], [22, 0, 0, 255], [24, 0, 0, 255], [26, 0, 0, 255], [28, 0, 0, 255], - [30, 0, 0, 255], [32, 0, 0, 255], [34, 0, 0, 255], [36, 0, 0, 255], [38, 0, 0, 255], - [40, 0, 0, 255], [42, 0, 0, 255], [44, 0, 0, 255], [46, 0, 0, 255], [48, 0, 0, 255], - [50, 0, 0, 255], [52, 0, 0, 255], [54, 0, 0, 255], [56, 0, 0, 255], [58, 0, 0, 255], - [60, 0, 0, 255], [62, 0, 0, 255], [64, 0, 0, 255], [66, 0, 0, 255], [68, 0, 0, 255], - [70, 0, 0, 255], [72, 0, 0, 255], [74, 0, 0, 255], [76, 0, 0, 255], [78, 0, 0, 255], - [80, 0, 0, 255], [82, 0, 0, 255], [84, 0, 0, 255], [86, 0, 0, 255], [88, 0, 0, 255], - [90, 0, 0, 255], [92, 0, 0, 255], [94, 0, 0, 255], [96, 0, 0, 255], [98, 0, 0, 255], - [100, 0, 0, 255], [102, 0, 0, 255], [104, 0, 0, 255], [106, 0, 0, 255], [108, 0, 0, 255], - [110, 0, 0, 255], [112, 0, 0, 255], [114, 0, 0, 255], [116, 0, 0, 255], [118, 0, 0, 255], - [120, 0, 0, 255], [122, 0, 0, 255], [124, 0, 0, 255], [126, 0, 0, 255], [128, 0, 0, 255], - [130, 0, 0, 255], [132, 0, 0, 255], [134, 0, 0, 255], [136, 0, 0, 255], [138, 0, 0, 255], - [140, 0, 0, 255], [142, 0, 0, 255], [144, 0, 0, 255], [146, 0, 0, 255], [148, 0, 0, 255], - [150, 0, 0, 255], [152, 0, 0, 255], [154, 0, 0, 255], [156, 0, 0, 255], [158, 0, 0, 255], - [160, 0, 0, 255], [162, 0, 0, 255], [164, 0, 0, 255], [166, 0, 0, 255], [168, 0, 0, 255], - [170, 0, 0, 255], [172, 0, 0, 255], [174, 0, 0, 255], [176, 0, 0, 255], [178, 0, 0, 255], - [180, 0, 0, 255], [182, 0, 0, 255], [184, 0, 0, 255], [186, 0, 0, 255], [188, 0, 0, 255], - [190, 0, 0, 255], [192, 0, 0, 255], [194, 0, 0, 255], [196, 0, 0, 255], [198, 0, 0, 255], - [200, 0, 0, 255], [202, 0, 0, 255], [204, 0, 0, 255], [206, 0, 0, 255], [208, 0, 0, 255], - [210, 0, 0, 255], [212, 0, 0, 255], [214, 0, 0, 255], [216, 0, 0, 255], [218, 0, 0, 255], - [220, 0, 0, 255], [222, 0, 0, 255], [224, 0, 0, 255], [226, 0, 0, 255], [228, 0, 0, 255], - [230, 0, 0, 255], [232, 0, 0, 255], [234, 0, 0, 255], [236, 0, 0, 255], [238, 0, 0, 255], - [240, 0, 0, 255], [242, 0, 0, 255], [244, 0, 0, 255], [246, 0, 0, 255], [248, 0, 0, 255], - [250, 0, 0, 255], [252, 0, 0, 255], [254, 0, 0, 255], [255, 0, 0, 255], [255, 2, 0, 255], - [255, 4, 0, 255], [255, 6, 0, 255], [255, 8, 0, 255], [255, 10, 0, 255], [255, 12, 0, 255], - [255, 14, 0, 255], [255, 16, 0, 255], [255, 18, 0, 255], [255, 20, 0, 255], [255, 22, 0, 255], - [255, 24, 0, 255], [255, 26, 0, 255], [255, 28, 0, 255], [255, 30, 0, 255], [255, 32, 0, 255], - [255, 34, 0, 255], [255, 36, 0, 255], [255, 38, 0, 255], [255, 40, 0, 255], [255, 42, 0, 255], - [255, 44, 0, 255], [255, 46, 0, 255], [255, 48, 0, 255], [255, 50, 0, 255], [255, 52, 0, 255], - [255, 54, 0, 255], [255, 56, 0, 255], [255, 58, 0, 255], [255, 60, 0, 255], [255, 62, 0, 255], - [255, 64, 0, 255], [255, 66, 0, 255], [255, 68, 0, 255], [255, 70, 0, 255], [255, 72, 0, 255], - [255, 74, 0, 255], [255, 76, 0, 255], [255, 78, 0, 255], [255, 80, 0, 255], [255, 82, 0, 255], - [255, 84, 0, 255], [255, 86, 0, 255], [255, 88, 0, 255], [255, 90, 0, 255], [255, 92, 0, 255], - [255, 94, 0, 255], [255, 96, 0, 255], [255, 98, 0, 255], [255, 100, 0, 255], [255, 102, 0, 255], - [255, 104, 0, 255], [255, 106, 0, 255], [255, 108, 0, 255], [255, 110, 0, 255], [255, 112, 0, 255], - [255, 114, 0, 255], [255, 116, 0, 255], [255, 118, 0, 255], [255, 120, 0, 255], [255, 122, 0, 255], - [255, 124, 0, 255], [255, 126, 0, 255], [255, 128, 4, 255], [255, 130, 8, 255], [255, 132, 12, 255], - [255, 134, 16, 255], [255, 136, 20, 255], [255, 138, 24, 255], [255, 140, 28, 255], [255, 142, 32, 255], - [255, 144, 36, 255], [255, 146, 40, 255], [255, 148, 44, 255], [255, 150, 48, 255], [255, 152, 52, 255], - [255, 154, 56, 255], [255, 156, 60, 255], [255, 158, 64, 255], [255, 160, 68, 255], [255, 162, 72, 255], - [255, 164, 76, 255], [255, 166, 80, 255], [255, 168, 84, 255], [255, 170, 88, 255], [255, 172, 92, 255], - [255, 174, 96, 255], [255, 176, 100, 255], [255, 178, 104, 255], [255, 180, 108, 255], [255, 182, 112, 255], - [255, 184, 116, 255], [255, 186, 120, 255], [255, 188, 124, 255], [255, 190, 128, 255], [255, 192, 132, 255], - [255, 194, 136, 255], [255, 196, 140, 255], [255, 198, 144, 255], [255, 200, 148, 255], [255, 202, 152, 255], - [255, 204, 156, 255], [255, 206, 160, 255], [255, 208, 164, 255], [255, 210, 168, 255], [255, 212, 172, 255], - [255, 214, 176, 255], [255, 216, 180, 255], [255, 218, 184, 255], [255, 220, 188, 255], [255, 222, 192, 255], - [255, 224, 196, 255], [255, 226, 200, 255], [255, 228, 204, 255], [255, 230, 208, 255], [255, 232, 212, 255], - [255, 234, 216, 255], [255, 236, 220, 255], [255, 238, 224, 255], [255, 240, 228, 255], [255, 242, 232, 255], - [255, 244, 236, 255], [255, 246, 240, 255], [255, 248, 244, 255], [255, 250, 248, 255], [255, 252, 252, 255], - [255, 255, 255, 255] - ] - }, - pet: { - name: 'PET', - numColors: 256, - colors: [ - [0, 0, 0, 255], [0, 2, 1, 255], [0, 4, 3, 255], [0, 6, 5, 255], [0, 8, 7, 255], - [0, 10, 9, 255], [0, 12, 11, 255], [0, 14, 13, 255], [0, 16, 15, 255], [0, 18, 17, 255], - [0, 20, 19, 255], [0, 22, 21, 255], [0, 24, 23, 255], [0, 26, 25, 255], [0, 28, 27, 255], - [0, 30, 29, 255], [0, 32, 31, 255], [0, 34, 33, 255], [0, 36, 35, 255], [0, 38, 37, 255], - [0, 40, 39, 255], [0, 42, 41, 255], [0, 44, 43, 255], [0, 46, 45, 255], [0, 48, 47, 255], - [0, 50, 49, 255], [0, 52, 51, 255], [0, 54, 53, 255], [0, 56, 55, 255], [0, 58, 57, 255], - [0, 60, 59, 255], [0, 62, 61, 255], [0, 65, 63, 255], [0, 67, 65, 255], [0, 69, 67, 255], - [0, 71, 69, 255], [0, 73, 71, 255], [0, 75, 73, 255], [0, 77, 75, 255], [0, 79, 77, 255], - [0, 81, 79, 255], [0, 83, 81, 255], [0, 85, 83, 255], [0, 87, 85, 255], [0, 89, 87, 255], - [0, 91, 89, 255], [0, 93, 91, 255], [0, 95, 93, 255], [0, 97, 95, 255], [0, 99, 97, 255], - [0, 101, 99, 255], [0, 103, 101, 255], [0, 105, 103, 255], [0, 107, 105, 255], [0, 109, 107, 255], - [0, 111, 109, 255], [0, 113, 111, 255], [0, 115, 113, 255], [0, 117, 115, 255], [0, 119, 117, 255], - [0, 121, 119, 255], [0, 123, 121, 255], [0, 125, 123, 255], [0, 128, 125, 255], [1, 126, 127, 255], - [3, 124, 129, 255], [5, 122, 131, 255], [7, 120, 133, 255], [9, 118, 135, 255], [11, 116, 137, 255], - [13, 114, 139, 255], [15, 112, 141, 255], [17, 110, 143, 255], [19, 108, 145, 255], [21, 106, 147, 255], - [23, 104, 149, 255], [25, 102, 151, 255], [27, 100, 153, 255], [29, 98, 155, 255], [31, 96, 157, 255], - [33, 94, 159, 255], [35, 92, 161, 255], [37, 90, 163, 255], [39, 88, 165, 255], [41, 86, 167, 255], - [43, 84, 169, 255], [45, 82, 171, 255], [47, 80, 173, 255], [49, 78, 175, 255], [51, 76, 177, 255], - [53, 74, 179, 255], [55, 72, 181, 255], [57, 70, 183, 255], [59, 68, 185, 255], [61, 66, 187, 255], - [63, 64, 189, 255], [65, 63, 191, 255], [67, 61, 193, 255], [69, 59, 195, 255], [71, 57, 197, 255], - [73, 55, 199, 255], [75, 53, 201, 255], [77, 51, 203, 255], [79, 49, 205, 255], [81, 47, 207, 255], - [83, 45, 209, 255], [85, 43, 211, 255], [86, 41, 213, 255], [88, 39, 215, 255], [90, 37, 217, 255], - [92, 35, 219, 255], [94, 33, 221, 255], [96, 31, 223, 255], [98, 29, 225, 255], [100, 27, 227, 255], - [102, 25, 229, 255], [104, 23, 231, 255], [106, 21, 233, 255], [108, 19, 235, 255], [110, 17, 237, 255], - [112, 15, 239, 255], [114, 13, 241, 255], [116, 11, 243, 255], [118, 9, 245, 255], [120, 7, 247, 255], - [122, 5, 249, 255], [124, 3, 251, 255], [126, 1, 253, 255], [128, 0, 255, 255], [130, 2, 252, 255], - [132, 4, 248, 255], [134, 6, 244, 255], [136, 8, 240, 255], [138, 10, 236, 255], [140, 12, 232, 255], - [142, 14, 228, 255], [144, 16, 224, 255], [146, 18, 220, 255], [148, 20, 216, 255], [150, 22, 212, 255], - [152, 24, 208, 255], [154, 26, 204, 255], [156, 28, 200, 255], [158, 30, 196, 255], [160, 32, 192, 255], - [162, 34, 188, 255], [164, 36, 184, 255], [166, 38, 180, 255], [168, 40, 176, 255], [170, 42, 172, 255], - [171, 44, 168, 255], [173, 46, 164, 255], [175, 48, 160, 255], [177, 50, 156, 255], [179, 52, 152, 255], - [181, 54, 148, 255], [183, 56, 144, 255], [185, 58, 140, 255], [187, 60, 136, 255], [189, 62, 132, 255], - [191, 64, 128, 255], [193, 66, 124, 255], [195, 68, 120, 255], [197, 70, 116, 255], [199, 72, 112, 255], - [201, 74, 108, 255], [203, 76, 104, 255], [205, 78, 100, 255], [207, 80, 96, 255], [209, 82, 92, 255], - [211, 84, 88, 255], [213, 86, 84, 255], [215, 88, 80, 255], [217, 90, 76, 255], [219, 92, 72, 255], - [221, 94, 68, 255], [223, 96, 64, 255], [225, 98, 60, 255], [227, 100, 56, 255], [229, 102, 52, 255], - [231, 104, 48, 255], [233, 106, 44, 255], [235, 108, 40, 255], [237, 110, 36, 255], [239, 112, 32, 255], - [241, 114, 28, 255], [243, 116, 24, 255], [245, 118, 20, 255], [247, 120, 16, 255], [249, 122, 12, 255], - [251, 124, 8, 255], [253, 126, 4, 255], [255, 128, 0, 255], [255, 130, 4, 255], [255, 132, 8, 255], - [255, 134, 12, 255], [255, 136, 16, 255], [255, 138, 20, 255], [255, 140, 24, 255], [255, 142, 28, 255], - [255, 144, 32, 255], [255, 146, 36, 255], [255, 148, 40, 255], [255, 150, 44, 255], [255, 152, 48, 255], - [255, 154, 52, 255], [255, 156, 56, 255], [255, 158, 60, 255], [255, 160, 64, 255], [255, 162, 68, 255], - [255, 164, 72, 255], [255, 166, 76, 255], [255, 168, 80, 255], [255, 170, 85, 255], [255, 172, 89, 255], - [255, 174, 93, 255], [255, 176, 97, 255], [255, 178, 101, 255], [255, 180, 105, 255], [255, 182, 109, 255], - [255, 184, 113, 255], [255, 186, 117, 255], [255, 188, 121, 255], [255, 190, 125, 255], [255, 192, 129, 255], - [255, 194, 133, 255], [255, 196, 137, 255], [255, 198, 141, 255], [255, 200, 145, 255], [255, 202, 149, 255], - [255, 204, 153, 255], [255, 206, 157, 255], [255, 208, 161, 255], [255, 210, 165, 255], [255, 212, 170, 255], - [255, 214, 174, 255], [255, 216, 178, 255], [255, 218, 182, 255], [255, 220, 186, 255], [255, 222, 190, 255], - [255, 224, 194, 255], [255, 226, 198, 255], [255, 228, 202, 255], [255, 230, 206, 255], [255, 232, 210, 255], - [255, 234, 214, 255], [255, 236, 218, 255], [255, 238, 222, 255], [255, 240, 226, 255], [255, 242, 230, 255], - [255, 244, 234, 255], [255, 246, 238, 255], [255, 248, 242, 255], [255, 250, 246, 255], [255, 252, 250, 255], - [255, 255, 255, 255] - ] - }, - hotMetalBlue: { - name: 'Hot Metal Blue', - numColors: 256, - colors: [ - [0, 0, 0, 255], [0, 0, 2, 255], [0, 0, 4, 255], [0, 0, 6, 255], [0, 0, 8, 255], - [0, 0, 10, 255], [0, 0, 12, 255], [0, 0, 14, 255], [0, 0, 16, 255], [0, 0, 17, 255], - [0, 0, 19, 255], [0, 0, 21, 255], [0, 0, 23, 255], [0, 0, 25, 255], [0, 0, 27, 255], - [0, 0, 29, 255], [0, 0, 31, 255], [0, 0, 33, 255], [0, 0, 35, 255], [0, 0, 37, 255], - [0, 0, 39, 255], [0, 0, 41, 255], [0, 0, 43, 255], [0, 0, 45, 255], [0, 0, 47, 255], - [0, 0, 49, 255], [0, 0, 51, 255], [0, 0, 53, 255], [0, 0, 55, 255], [0, 0, 57, 255], - [0, 0, 59, 255], [0, 0, 61, 255], [0, 0, 63, 255], [0, 0, 65, 255], [0, 0, 67, 255], - [0, 0, 69, 255], [0, 0, 71, 255], [0, 0, 73, 255], [0, 0, 75, 255], [0, 0, 77, 255], - [0, 0, 79, 255], [0, 0, 81, 255], [0, 0, 83, 255], [0, 0, 84, 255], [0, 0, 86, 255], - [0, 0, 88, 255], [0, 0, 90, 255], [0, 0, 92, 255], [0, 0, 94, 255], [0, 0, 96, 255], - [0, 0, 98, 255], [0, 0, 100, 255], [0, 0, 102, 255], [0, 0, 104, 255], [0, 0, 106, 255], - [0, 0, 108, 255], [0, 0, 110, 255], [0, 0, 112, 255], [0, 0, 114, 255], [0, 0, 116, 255], - [0, 0, 117, 255], [0, 0, 119, 255], [0, 0, 121, 255], [0, 0, 123, 255], [0, 0, 125, 255], - [0, 0, 127, 255], [0, 0, 129, 255], [0, 0, 131, 255], [0, 0, 133, 255], [0, 0, 135, 255], - [0, 0, 137, 255], [0, 0, 139, 255], [0, 0, 141, 255], [0, 0, 143, 255], [0, 0, 145, 255], - [0, 0, 147, 255], [0, 0, 149, 255], [0, 0, 151, 255], [0, 0, 153, 255], [0, 0, 155, 255], - [0, 0, 157, 255], [0, 0, 159, 255], [0, 0, 161, 255], [0, 0, 163, 255], [0, 0, 165, 255], - [0, 0, 167, 255], [3, 0, 169, 255], [6, 0, 171, 255], [9, 0, 173, 255], [12, 0, 175, 255], - [15, 0, 177, 255], [18, 0, 179, 255], [21, 0, 181, 255], [24, 0, 183, 255], [26, 0, 184, 255], - [29, 0, 186, 255], [32, 0, 188, 255], [35, 0, 190, 255], [38, 0, 192, 255], [41, 0, 194, 255], - [44, 0, 196, 255], [47, 0, 198, 255], [50, 0, 200, 255], [52, 0, 197, 255], [55, 0, 194, 255], - [57, 0, 191, 255], [59, 0, 188, 255], [62, 0, 185, 255], [64, 0, 182, 255], [66, 0, 179, 255], - [69, 0, 176, 255], [71, 0, 174, 255], [74, 0, 171, 255], [76, 0, 168, 255], [78, 0, 165, 255], - [81, 0, 162, 255], [83, 0, 159, 255], [85, 0, 156, 255], [88, 0, 153, 255], [90, 0, 150, 255], - [93, 2, 144, 255], [96, 4, 138, 255], [99, 6, 132, 255], [102, 8, 126, 255], [105, 9, 121, 255], - [108, 11, 115, 255], [111, 13, 109, 255], [114, 15, 103, 255], [116, 17, 97, 255], [119, 19, 91, 255], - [122, 21, 85, 255], [125, 23, 79, 255], [128, 24, 74, 255], [131, 26, 68, 255], [134, 28, 62, 255], - [137, 30, 56, 255], [140, 32, 50, 255], [143, 34, 47, 255], [146, 36, 44, 255], [149, 38, 41, 255], - [152, 40, 38, 255], [155, 41, 35, 255], [158, 43, 32, 255], [161, 45, 29, 255], [164, 47, 26, 255], - [166, 49, 24, 255], [169, 51, 21, 255], [172, 53, 18, 255], [175, 55, 15, 255], [178, 56, 12, 255], - [181, 58, 9, 255], [184, 60, 6, 255], [187, 62, 3, 255], [190, 64, 0, 255], [194, 66, 0, 255], - [198, 68, 0, 255], [201, 70, 0, 255], [205, 72, 0, 255], [209, 73, 0, 255], [213, 75, 0, 255], - [217, 77, 0, 255], [221, 79, 0, 255], [224, 81, 0, 255], [228, 83, 0, 255], [232, 85, 0, 255], - [236, 87, 0, 255], [240, 88, 0, 255], [244, 90, 0, 255], [247, 92, 0, 255], [251, 94, 0, 255], - [255, 96, 0, 255], [255, 98, 3, 255], [255, 100, 6, 255], [255, 102, 9, 255], [255, 104, 12, 255], - [255, 105, 15, 255], [255, 107, 18, 255], [255, 109, 21, 255], [255, 111, 24, 255], [255, 113, 26, 255], - [255, 115, 29, 255], [255, 117, 32, 255], [255, 119, 35, 255], [255, 120, 38, 255], [255, 122, 41, 255], - [255, 124, 44, 255], [255, 126, 47, 255], [255, 128, 50, 255], [255, 130, 53, 255], [255, 132, 56, 255], - [255, 134, 59, 255], [255, 136, 62, 255], [255, 137, 65, 255], [255, 139, 68, 255], [255, 141, 71, 255], - [255, 143, 74, 255], [255, 145, 76, 255], [255, 147, 79, 255], [255, 149, 82, 255], [255, 151, 85, 255], - [255, 152, 88, 255], [255, 154, 91, 255], [255, 156, 94, 255], [255, 158, 97, 255], [255, 160, 100, 255], - [255, 162, 103, 255], [255, 164, 106, 255], [255, 166, 109, 255], [255, 168, 112, 255], [255, 169, 115, 255], - [255, 171, 118, 255], [255, 173, 121, 255], [255, 175, 124, 255], [255, 177, 126, 255], [255, 179, 129, 255], - [255, 181, 132, 255], [255, 183, 135, 255], [255, 184, 138, 255], [255, 186, 141, 255], [255, 188, 144, 255], - [255, 190, 147, 255], [255, 192, 150, 255], [255, 194, 153, 255], [255, 196, 156, 255], [255, 198, 159, 255], - [255, 200, 162, 255], [255, 201, 165, 255], [255, 203, 168, 255], [255, 205, 171, 255], [255, 207, 174, 255], - [255, 209, 176, 255], [255, 211, 179, 255], [255, 213, 182, 255], [255, 215, 185, 255], [255, 216, 188, 255], - [255, 218, 191, 255], [255, 220, 194, 255], [255, 222, 197, 255], [255, 224, 200, 255], [255, 226, 203, 255], - [255, 228, 206, 255], [255, 229, 210, 255], [255, 231, 213, 255], [255, 233, 216, 255], [255, 235, 219, 255], - [255, 237, 223, 255], [255, 239, 226, 255], [255, 240, 229, 255], [255, 242, 232, 255], [255, 244, 236, 255], - [255, 246, 239, 255], [255, 248, 242, 255], [255, 250, 245, 255], [255, 251, 249, 255], [255, 253, 252, 255], - [255, 255, 255, 255] - ] - }, - pet20Step: { - name: 'PET 20 Step', - numColors: 256, - colors: [ - [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], - [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], - [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [96, 0, 80, 255], [96, 0, 80, 255], - [96, 0, 80, 255], [96, 0, 80, 255], [96, 0, 80, 255], [96, 0, 80, 255], [96, 0, 80, 255], - [96, 0, 80, 255], [96, 0, 80, 255], [96, 0, 80, 255], [96, 0, 80, 255], [96, 0, 80, 255], - [96, 0, 80, 255], [48, 48, 80, 255], [48, 48, 80, 255], [48, 48, 80, 255], [48, 48, 80, 255], - [48, 48, 80, 255], [48, 48, 80, 255], [48, 48, 80, 255], [48, 48, 80, 255], [48, 48, 80, 255], - [48, 48, 80, 255], [48, 48, 80, 255], [48, 48, 80, 255], [48, 48, 80, 255], [48, 48, 112, 255], - [48, 48, 112, 255], [48, 48, 112, 255], [48, 48, 112, 255], [48, 48, 112, 255], [48, 48, 112, 255], - [48, 48, 112, 255], [48, 48, 112, 255], [48, 48, 112, 255], [48, 48, 112, 255], [48, 48, 112, 255], - [48, 48, 112, 255], [80, 80, 128, 255], [80, 80, 128, 255], [80, 80, 128, 255], [80, 80, 128, 255], - [80, 80, 128, 255], [80, 80, 128, 255], [80, 80, 128, 255], [80, 80, 128, 255], [80, 80, 128, 255], - [80, 80, 128, 255], [80, 80, 128, 255], [80, 80, 128, 255], [80, 80, 128, 255], [96, 96, 176, 255], - [96, 96, 176, 255], [96, 96, 176, 255], [96, 96, 176, 255], [96, 96, 176, 255], [96, 96, 176, 255], - [96, 96, 176, 255], [96, 96, 176, 255], [96, 96, 176, 255], [96, 96, 176, 255], [96, 96, 176, 255], - [96, 96, 176, 255], [96, 96, 176, 255], [112, 112, 192, 255], [112, 112, 192, 255], [112, 112, 192, 255], - [112, 112, 192, 255], [112, 112, 192, 255], [112, 112, 192, 255], [112, 112, 192, 255], [112, 112, 192, 255], - [112, 112, 192, 255], [112, 112, 192, 255], [112, 112, 192, 255], [112, 112, 192, 255], [112, 112, 192, 255], - [128, 128, 224, 255], [128, 128, 224, 255], [128, 128, 224, 255], [128, 128, 224, 255], [128, 128, 224, 255], - [128, 128, 224, 255], [128, 128, 224, 255], [128, 128, 224, 255], [128, 128, 224, 255], [128, 128, 224, 255], - [128, 128, 224, 255], [128, 128, 224, 255], [48, 96, 48, 255], [48, 96, 48, 255], [48, 96, 48, 255], - [48, 96, 48, 255], [48, 96, 48, 255], [48, 96, 48, 255], [48, 96, 48, 255], [48, 96, 48, 255], - [48, 96, 48, 255], [48, 96, 48, 255], [48, 96, 48, 255], [48, 96, 48, 255], [48, 96, 48, 255], - [48, 144, 48, 255], [48, 144, 48, 255], [48, 144, 48, 255], [48, 144, 48, 255], [48, 144, 48, 255], - [48, 144, 48, 255], [48, 144, 48, 255], [48, 144, 48, 255], [48, 144, 48, 255], [48, 144, 48, 255], - [48, 144, 48, 255], [48, 144, 48, 255], [48, 144, 48, 255], [80, 192, 80, 255], [80, 192, 80, 255], - [80, 192, 80, 255], [80, 192, 80, 255], [80, 192, 80, 255], [80, 192, 80, 255], [80, 192, 80, 255], - [80, 192, 80, 255], [80, 192, 80, 255], [80, 192, 80, 255], [80, 192, 80, 255], [80, 192, 80, 255], - [80, 192, 80, 255], [64, 224, 64, 255], [64, 224, 64, 255], [64, 224, 64, 255], [64, 224, 64, 255], - [64, 224, 64, 255], [64, 224, 64, 255], [64, 224, 64, 255], [64, 224, 64, 255], [64, 224, 64, 255], - [64, 224, 64, 255], [64, 224, 64, 255], [64, 224, 64, 255], [224, 224, 80, 255], [224, 224, 80, 255], - [224, 224, 80, 255], [224, 224, 80, 255], [224, 224, 80, 255], [224, 224, 80, 255], [224, 224, 80, 255], - [224, 224, 80, 255], [224, 224, 80, 255], [224, 224, 80, 255], [224, 224, 80, 255], [224, 224, 80, 255], - [224, 224, 80, 255], [208, 208, 96, 255], [208, 208, 96, 255], [208, 208, 96, 255], [208, 208, 96, 255], - [208, 208, 96, 255], [208, 208, 96, 255], [208, 208, 96, 255], [208, 208, 96, 255], [208, 208, 96, 255], - [208, 208, 96, 255], [208, 208, 96, 255], [208, 208, 96, 255], [208, 208, 96, 255], [208, 176, 64, 255], - [208, 176, 64, 255], [208, 176, 64, 255], [208, 176, 64, 255], [208, 176, 64, 255], [208, 176, 64, 255], - [208, 176, 64, 255], [208, 176, 64, 255], [208, 176, 64, 255], [208, 176, 64, 255], [208, 176, 64, 255], - [208, 176, 64, 255], [208, 176, 64, 255], [208, 144, 0, 255], [208, 144, 0, 255], [208, 144, 0, 255], - [208, 144, 0, 255], [208, 144, 0, 255], [208, 144, 0, 255], [208, 144, 0, 255], [208, 144, 0, 255], - [208, 144, 0, 255], [208, 144, 0, 255], [208, 144, 0, 255], [208, 144, 0, 255], [192, 96, 0, 255], - [192, 96, 0, 255], [192, 96, 0, 255], [192, 96, 0, 255], [192, 96, 0, 255], [192, 96, 0, 255], - [192, 96, 0, 255], [192, 96, 0, 255], [192, 96, 0, 255], [192, 96, 0, 255], [192, 96, 0, 255], - [192, 96, 0, 255], [192, 96, 0, 255], [176, 48, 0, 255], [176, 48, 0, 255], [176, 48, 0, 255], - [176, 48, 0, 255], [176, 48, 0, 255], [176, 48, 0, 255], [176, 48, 0, 255], [176, 48, 0, 255], - [176, 48, 0, 255], [176, 48, 0, 255], [176, 48, 0, 255], [176, 48, 0, 255], [176, 48, 0, 255], - [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], - [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], - [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], - [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], - [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], - [255, 255, 255, 255] - ] - }, - gray: { - name: 'Gray', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0, 0], [1, 1, 1]], - 'green': [[0, 0, 0], [1, 1, 1]], - 'blue': [[0, 0, 0], [1, 1, 1]] - } - }, - jet: { - name: 'Jet', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0, 0], [0.35 , 0, 0], [0.66 , 1, 1], [0.89, 1, 1], [1, 0.5, 0.5]], - 'green': [[0, 0, 0], [0.125, 0, 0], [0.375, 1, 1], [0.64, 1, 1], [0.91, 0, 0], [1, 0, 0]], - 'blue': [[0, 0.5, 0.5], [0.11 , 1, 1], [0.34 , 1, 1], [0.65, 0, 0], [1, 0, 0]] - } - }, - hsv: { - name: 'HSV', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 1, 1], [0.158730, 1, 1], [0.174603, 0.968750, 0.968750], - [0.333333, 0.031250, 0.031250], [0.349206, 0, 0], [0.666667, 0, 0], - [0.682540, 0.031250, 0.031250],[0.841270, 0.968750, 0.968750], - [0.857143, 1, 1], [1, 1, 1]], - 'green': [[0, 0, 0],[0.158730, 0.937500, 0.937500], [0.174603, 1, 1], - [0.507937, 1, 1], [0.666667, 0.062500, 0.062500], - [0.682540, 0, 0], [1, 0, 0]], - 'blue': [[0, 0, 0], [0.333333, 0, 0], [0.349206, 0.062500, 0.062500], - [0.507937, 1, 1], [0.841270, 1, 1], [0.857143, 0.937500, 0.937500], - [1, 0.09375, 0.09375]] - } - }, - hot: { - name: 'Hot', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0.0416, 0.0416], [0.365079, 1, 1], [1, 1, 1]], - 'green': [[0, 0, 0], [0.365079, 0, 0], [0.746032, 1, 1], [1, 1, 1]], - 'blue': [[0, 0, 0], [0.746032, 0, 0], [1, 1, 1]] - } - }, - cool: { - name: 'Cool', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0, 0], [1, 1, 1]], - 'green': [[0, 1, 1], [1, 0, 0]], - 'blue': [[0, 1, 1], [1, 1, 1]] - } - }, - spring: { - name: 'Spring', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 1, 1],[1, 1, 1]], - 'green': [[0, 0, 0],[1, 1, 1]], - 'blue': [[0, 1, 1],[1, 0, 0]] - } - }, - summer: { - name: 'Summer', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0, 0],[1, 1, 1]], - 'green': [[0, 0.5, 0.5],[1, 1, 1]], - 'blue': [[0, 0.4, 0.4],[1, 0.4, 0.4]] - } - }, - autumn: { - name: 'Autumn', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 1, 1], [1, 1, 1]], - 'green': [[0, 0, 0], [1, 1, 1]], - 'blue': [[0, 0, 0], [1, 0, 0]] - } - }, - winter: { - name: 'Winter', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0, 0],[1, 0, 0]], - 'green': [[0, 0, 0],[1, 1, 1]], - 'blue': [[0, 1, 1],[1, 0.5, 0.5]] - } - }, - bone: { - name: 'Bone', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0, 0], [0.746032, 0.652778, 0.652778], [1, 1, 1]], - 'green': [[0, 0, 0], [0.365079, 0.319444, 0.319444], [0.746032, 0.777778, 0.777778], [1, 1, 1]], - 'blue': [[0, 0, 0], [0.365079, 0.444444, 0.444444], [1, 1, 1]] - } - }, - copper: { - name: 'Copper', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0, 0], [0.809524, 1, 1], [1, 1, 1]], - 'green': [[0, 0, 0], [1, 0.7812, 0.7812]], - 'blue': [[0, 0, 0], [1, 0.4975, 0.4975]] - } - }, - spectral: { - name: 'Spectral', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0, 0], [0.05, 0.4667, 0.4667], [0.10, 0.5333, 0.5333], [0.15, 0, 0], - [0.20, 0, 0], [0.25, 0, 0], [0.30, 0, 0], [0.35, 0, 0], - [0.40, 0, 0], [0.45, 0, 0], [0.50, 0, 0], [0.55, 0, 0], - [0.60, 0, 0], [0.65, 0.7333, 0.7333], [0.70, 0.9333, 0.9333], [0.75, 1, 1], - [0.80, 1, 1], [0.85, 1, 1], [0.90, 0.8667, 0.8667], [0.95, 0.80, 0.80], - [1, 0.80, 0.80]], - 'green': [[0, 0, 0], [0.05, 0, 0], [0.10, 0, 0], [0.15, 0, 0], - [0.20, 0, 0], [0.25, 0.4667, 0.4667], [0.30, 0.6000, 0.6000], - [0.35, 0.6667, 0.6667], [0.40, 0.6667, 0.6667], [0.45, 0.6000, 0.6000], - [0.50, 0.7333, 0.7333], [0.55, 0.8667, 0.8667], [0.60, 1, 1], [0.65, 1, 1], - [0.70, 0.9333, 0.9333], [0.75, 0.8000, 0.8000], - [0.80, 0.6000, 0.6000], [0.85, 0, 0], - [0.90, 0, 0], [0.95, 0, 0], [1, 0.80, 0.80]], - 'blue': [[0, 0, 0], [0.05, 0.5333, 0.5333], [0.10, 0.6000, 0.6000], [0.15, 0.6667, 0.6667], - [0.20, 0.8667, 0.8667], [0.25, 0.8667, 0.8667], [0.30, 0.8667, 0.8667], - [0.35, 0.6667, 0.6667], [0.40, 0.5333, 0.5333], [0.45, 0, 0], - [0.5, 0, 0], [0.55, 0, 0], [0.60, 0, 0], [0.65, 0, 0], [0.70, 0, 0], [0.75, 0, 0], - [0.80, 0, 0], [0.85, 0, 0], [0.90, 0, 0], [0.95, 0, 0], [1, 0.80, 0.80]] - } - }, - coolwarm: { - name: 'CoolWarm', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0.2298057, 0.2298057], [0.03125, 0.26623388, 0.26623388], - [0.0625, 0.30386891, 0.30386891], [0.09375, 0.342804478, 0.342804478], - [0.125, 0.38301334, 0.38301334], [0.15625, 0.424369608, 0.424369608], - [0.1875, 0.46666708, 0.46666708], [0.21875, 0.509635204, 0.509635204], - [0.25, 0.552953156, 0.552953156], [0.28125, 0.596262162, 0.596262162], - [0.3125, 0.639176211, 0.639176211], [0.34375, 0.681291281, 0.681291281], - [0.375, 0.722193294, 0.722193294], [0.40625, 0.761464949, 0.761464949], - [0.4375, 0.798691636, 0.798691636], [0.46875, 0.833466556, 0.833466556], - [0.5, 0.865395197, 0.865395197], [0.53125, 0.897787179, 0.897787179], - [0.5625, 0.924127593, 0.924127593], [0.59375, 0.944468518, 0.944468518], - [0.625, 0.958852946, 0.958852946], [0.65625, 0.96732803, 0.96732803], - [0.6875, 0.969954137, 0.969954137], [0.71875, 0.966811177, 0.966811177], - [0.75, 0.958003065, 0.958003065], [0.78125, 0.943660866, 0.943660866], - [0.8125, 0.923944917, 0.923944917], [0.84375, 0.89904617, 0.89904617], - [0.875, 0.869186849, 0.869186849], [0.90625, 0.834620542, 0.834620542], - [0.9375, 0.795631745, 0.795631745], [0.96875, 0.752534934, 0.752534934], - [1, 0.705673158, 0.705673158]], - 'green': [[0, 0.298717966, 0.298717966], [0.03125, 0.353094838, 0.353094838], - [0.0625, 0.406535296, 0.406535296], [0.09375, 0.458757618, 0.458757618], - [0.125, 0.50941904, 0.50941904], [0.15625, 0.558148092, 0.558148092], - [0.1875, 0.604562568, 0.604562568], [0.21875, 0.648280772, 0.648280772], - [0.25, 0.688929332, 0.688929332], [0.28125, 0.726149107, 0.726149107], - [0.3125, 0.759599947, 0.759599947], [0.34375, 0.788964712, 0.788964712], - [0.375, 0.813952739, 0.813952739], [0.40625, 0.834302879, 0.834302879], - [0.4375, 0.849786142, 0.849786142], [0.46875, 0.860207984, 0.860207984], - [0.5, 0.86541021, 0.86541021], [0.53125, 0.848937047, 0.848937047], - [0.5625, 0.827384882, 0.827384882], [0.59375, 0.800927443, 0.800927443], - [0.625, 0.769767752, 0.769767752], [0.65625, 0.734132809, 0.734132809], - [0.6875, 0.694266682, 0.694266682], [0.71875, 0.650421156, 0.650421156], - [0.75, 0.602842431, 0.602842431], [0.78125, 0.551750968, 0.551750968], - [0.8125, 0.49730856, 0.49730856], [0.84375, 0.439559467, 0.439559467], - [0.875, 0.378313092, 0.378313092], [0.90625, 0.312874446, 0.312874446], - [0.9375, 0.24128379, 0.24128379], [0.96875, 0.157246067, 0.157246067], - [1, 0.01555616, 0.01555616]], - 'blue': [[0, 0.753683153, 0.753683153], [0.03125, 0.801466763, 0.801466763], - [0.0625, 0.84495867, 0.84495867], [0.09375, 0.883725899, 0.883725899], - [0.125, 0.917387822, 0.917387822], [0.15625, 0.945619588, 0.945619588], - [0.1875, 0.968154911, 0.968154911], [0.21875, 0.98478814, 0.98478814], - [0.25, 0.995375608, 0.995375608], [0.28125, 0.999836203, 0.999836203], - [0.3125, 0.998151185, 0.998151185], [0.34375, 0.990363227, 0.990363227], - [0.375, 0.976574709, 0.976574709], [0.40625, 0.956945269, 0.956945269], - [0.4375, 0.931688648, 0.931688648], [0.46875, 0.901068838, 0.901068838], - [0.5, 0.865395561, 0.865395561], [0.53125, 0.820880546, 0.820880546], - [0.5625, 0.774508472, 0.774508472], [0.59375, 0.726736146, 0.726736146], - [0.625, 0.678007945, 0.678007945], [0.65625, 0.628751763, 0.628751763], - [0.6875, 0.579375448, 0.579375448], [0.71875, 0.530263762, 0.530263762], - [0.75, 0.481775914, 0.481775914], [0.78125, 0.434243684, 0.434243684], - [0.8125, 0.387970225, 0.387970225], [0.84375, 0.343229596, 0.343229596], - [0.875, 0.300267182, 0.300267182], [0.90625, 0.259301199, 0.259301199], - [0.9375, 0.220525627, 0.220525627], [0.96875, 0.184115123, 0.184115123], - [1, 0.150232812, 0.150232812]] - } - }, - blues: { - name: 'Blues', - numColors: 256, - gamma: 1, - segmentedData: { - 'red': [[0, 0.9686274528503418, 0.9686274528503418], [0.125, 0.87058824300765991, 0.87058824300765991], - [0.25, 0.7764706015586853, 0.7764706015586853], [0.375, 0.61960786581039429, 0.61960786581039429], - [0.5, 0.41960784792900085, 0.41960784792900085], [0.625, 0.25882354378700256, 0.25882354378700256], - [0.75, 0.12941177189350128, 0.12941177189350128], [0.875, 0.031372550874948502, 0.031372550874948502], - [1, 0.031372550874948502, 0.031372550874948502]], - 'green': [[0, 0.9843137264251709, 0.9843137264251709], [0.125, 0.92156863212585449, 0.92156863212585449], - [0.25, 0.85882353782653809, 0.85882353782653809], [0.375, 0.7921568751335144, 0.7921568751335144], - [0.5, 0.68235296010971069, 0.68235296010971069], [0.625, 0.57254904508590698, 0.57254904508590698], - [0.75, 0.44313725829124451, 0.44313725829124451], [0.875, 0.31764706969261169, 0.31764706969261169], - [1, 0.18823529779911041, 0.18823529779911041]], - 'blue': [[0, 1, 1], [0.125, 0.9686274528503418, 0.9686274528503418], [0.25, 0.93725490570068359, 0.93725490570068359], - [0.375, 0.88235294818878174, 0.88235294818878174], [0.5, 0.83921569585800171, 0.83921569585800171], - [0.625, 0.7764706015586853, 0.7764706015586853], [0.75, 0.70980393886566162, 0.70980393886566162], - [0.875, 0.61176472902297974, 0.61176472902297974], [1, 0.41960784792900085, 0.41960784792900085]] - } - } - } - - // Generate linearly spaced vectors - // http://cens.ioc.ee/local/man/matlab/techdoc/ref/linspace.html - function linspace(a, b, n) { - n = n == null ? 100 : n; - - var increment = (b - a) / (n - 1); - var vector = []; - - while(n-- > 0) { - vector.push(a); - a += increment; - } - - // Make sure the last item will always be "b" because most of the - // time we'll get numbers like 1.0000000000000002 instead of 1. - vector[vector.length - 1] = b; - - return vector; - } - - // Return the number of elements smaller than "elem" (binary search) - function getRank(array, elem) { - var left = 0; - var right = array.length - 1; - - while(left <= right) { - var mid = left + Math.floor((right - left) / 2); - var midElem = array[mid]; - - if(midElem === elem) { - return mid; - } else if(elem < midElem) { - right = mid - 1; - } else { - left = mid + 1; - } - } - - return left; - } - - // Find the indices into a sorted array a such that, if the corresponding elements - // in v were inserted before the indices, the order of a would be preserved. - // http://lagrange.univ-lyon1.fr/docs/numpy/1.11.0/reference/generated/numpy.searchsorted.html - function searchSorted(inputArray, values) { - var i; - var indexes = []; - var len = values.length; - - inputArray.sort(function(a, b) { - return a - b; - }); - - for(i = 0; i < len; i++) { - indexes[i] = getRank(inputArray, values[i]); - } - - return indexes; - } - - // Create an *N* -element 1-d lookup table - // - // *data* represented by a list of x,y0,y1 mapping correspondences. Each element in this - // list represents how a value between 0 and 1 (inclusive) represented by x is mapped to - // a corresponding value between 0 and 1 (inclusive). The two values of y are to allow for - // discontinuous mapping functions (say as might be found in a sawtooth) where y0 represents - // the value of y for values of x <= to that given, and y1 is the value to be used for x > - // than that given). The list must start with x=0, end with x=1, and all values of x must be - // in increasing order. Values between the given mapping points are determined by simple linear - // interpolation. - // - // The function returns an array "result" where result[x*(N-1)] gives the closest value for - // values of x between 0 and 1. - function makeMappingArray(N, data, gamma) { - var i; - var x = []; - var y0 = []; - var y1 = []; - var lut = []; - - gamma = gamma == null ? 1 : gamma; - - for(i = 0; i < data.length; i++) { - var element = data[i]; - - x.push((N - 1) * element[0]); - y0.push(element[1]); - y1.push(element[1]); - } - - var xLinSpace = linspace(0, 1, N); - for(i = 0; i < N; i++) { - xLinSpace[i] = (N - 1) * Math.pow(xLinSpace[i], gamma); - } - - var xLinSpaceIndexes = searchSorted(x, xLinSpace); - - for(i = 1; i < N - 1; i++) { - var index = xLinSpaceIndexes[i]; - var colorPercent = ((xLinSpace[i] - x[index - 1]) / (x[index] - x[index - 1])); - var colorDelta = (y0[index] - y1[index - 1]) - - lut[i] = colorPercent * colorDelta + y1[index - 1]; - } - - lut[0] = y1[0]; - lut[N-1] = y0[data.length - 1]; - - return lut; - } - - // Colormap based on lookup tables using linear segments. - // - // The lookup table is generated using linear interpolation for each - // primary color, with the 0-1 domain divided into any number of - // segments. - // - // https://github.com/stefanv/matplotlib/blob/3f1a23755e86fef97d51e30e106195f34425c9e3/lib/matplotlib/colors.py#L663 - function createLinearSegmentedColormap(segmentedData, N, gamma) { - var i; - var lut = []; - - N = N == null ? 256 : N; - gamma = gamma == null ? 1 : gamma; - - var redLut = makeMappingArray(N, segmentedData.red, gamma); - var greenLut = makeMappingArray(N, segmentedData.green, gamma); - var blueLut = makeMappingArray(N, segmentedData.blue, gamma); - - for(i = 0; i < N; i++) { - var red = Math.round(redLut[i] * 255); - var green = Math.round(greenLut[i] * 255); - var blue = Math.round(blueLut[i] * 255); - var rgba = [red, green, blue, 255]; - - lut.push(rgba); - } - - return lut; - } - - /* - * Return all colormaps (id and name) available - */ - function getColormapsList() { - var colormaps = []; - var keys = Object.keys(colormapsData); - - keys.forEach(function(key) { - if(colormapsData.hasOwnProperty(key)) { - var colormap = colormapsData[key]; - - colormaps.push({ - id: key, - name: colormap.name - }); - } - }); - - colormaps.sort(function(a, b) { - var aName = a.name.toLowerCase(); - var bName = b.name.toLowerCase(); - - if(aName === bName) { - return 0; - } - - return aName < bName ? -1 : 1;; - }); - - return colormaps; - } - - /** - * Convert the image of a element to a false color image - * @param id - * @param colormapData - An object that can contain a name, numColors, gama, segmentedData and/or colors - */ - function getColormap(id, colormapData) { - var colormap = colormapsData[id]; - - if(!colormap) { - colormap = colormapsData[id] = colormapData || { - name: '', - colors: [] - } - } - - if(!colormap.colors && colormap.segmentedData) { - colormap.colors = createLinearSegmentedColormap(colormap.segmentedData, colormap.numColors, colormap.gamma); - } - - return { - getId: function() { - return id; - }, - - getColorSchemeName: function(index) { - return colormap.name; - }, - - setColorSchemeName: function(name) { - colormap.name = name; - }, - - getNumberOfColors: function() { - return colormap.colors.length; - }, - - setNumberOfColors: function(numColors) { - while(colormap.colors.length < numColors) { - colormap.colors.push(COLOR_BLACK); - } - - colormap.colors.length = numColors; - }, - - getColor: function(index) { - if(this.isValidIndex(index)) { - return colormap.colors[index]; - } - - return COLOR_BLACK; - }, - - getColorRepeating: function(index) { - var numColors = colormap.colors.length; - index = numColors ? index % numColors : 0; - - return this.getColor(index); - }, - - setColor: function(index, rgba) { - if(this.isValidIndex(index)) { - colormap.colors[index] = rgba; - } - }, - - addColor: function(rgba) { - colormap.colors.push(rgba); - }, - - insertColor: function(index, rgba) { - if(this.isValidIndex(index)) { - colormap.colors.splice(index, 1, rgba); - } - }, - - removeColor: function(index) { - if(this.isValidIndex(index)) { - colormap.colors.splice(index, 1); - } - }, - - clearColors: function() { - colormap.colors = []; - }, - - buildLookupTable: function(lut) { - if(!lut) { - return; - } - - var i; - var numColors = colormap.colors.length; - - lut.setNumberOfTableValues(numColors); - - for(i = 0; i < numColors; i++) { - lut.setTableValue(i, colormap.colors[i]); - } - }, - - createLookupTable: function() { - var lut = new cornerstone.colors.LookupTable(); - this.buildLookupTable(lut); - - return lut; - }, - - isValidIndex: function(index) { - return (index >= 0) && (index < colormap.colors.length); - } - } - } - - cornerstone.colors.getColormap = getColormap; - cornerstone.colors.getColormapsList = getColormapsList; - -}(cornerstone)); - -// This code was created based on vtkLookupTable -// http://www.vtk.org/doc/release/5.0/html/a01697.html -// https://github.com/Kitware/VTK/blob/master/Common/Core/vtkLookupTable.cxx - -(function(cornerstone) { - - "use strict"; - - var BELOW_RANGE_COLOR_INDEX = 0; - var ABOVE_RANGE_COLOR_INDEX = 1; - var NAN_COLOR_INDEX = 2; - var NUMBER_OF_SPECIAL_COLORS = NAN_COLOR_INDEX + 1; - - function LookupTable() { - this.NumberOfColors = 256; - this.Ramp = 'linear'; - this.TableRange = [0, 255]; - this.HueRange = [0, 0.66667]; - this.SaturationRange = [1, 1]; - this.ValueRange = [1, 1]; - this.AlphaRange = [1, 1]; - this.NaNColor = [128, 0, 0, 255]; - this.BelowRangeColor = [0, 0, 0, 255]; - this.UseBelowRangeColor = true; - this.AboveRangeColor = [255, 255, 255, 255]; - this.UseAboveRangeColor = true; - this.InputRange = [0, 255]; - this.Table = []; - - - this.setNumberOfTableValues = function(number) { - this.NumberOfColors = number; - }; - - this.setRamp = function(ramp) { - this.Ramp = ramp; - }; - - this.setTableRange = function(start, end) { - // Set/Get the minimum/maximum scalar values for scalar mapping. - // Scalar values less than minimum range value are clamped to minimum range value. - // Scalar values greater than maximum range value are clamped to maximum range value. - this.TableRange[0] = start; - this.TableRange[1] = end; - }; - - this.setHueRange = function(start, end) { - // Set the range in hue (using automatic generation). Hue ranges between [0,1]. - this.HueRange[0] = start; - this.HueRange[1] = end; - }; - - this.setSaturationRange = function(start, end) { - // Set the range in saturation (using automatic generation). Saturation ranges between [0,1]. - this.SaturationRange[0] = start; - this.SaturationRange[1] = end; - }; - - this.setValueRange = function(start, end) { - // Set the range in value (using automatic generation). Value ranges between [0,1]. - this.ValueRange[0] = start; - this.ValueRange[1] = end; - }; - - this.setRange = function(start, end) { - this.InputRange[0] = start; - this.InputRange[1] = end; - }; - - this.setAlphaRange = function(start, end) { - // Set the range in alpha (using automatic generation). Alpha ranges from [0,1]. - this.AlphaRange[0] = start; - this.AlphaRange[1] = end; - }; - - this.getColor = function(scalar) { - // Map one value through the lookup table and return the color as an - // RGB array of doubles between 0 and 1. - - return this.mapValue(scalar); - }; - - this.getOpacity = function() { - return opacity; - }; - - this.HSVToRGB = function(hue, sat, val) { - if (hue > 1) { - throw "HSVToRGB expects hue < 1"; - } - - var rgb = []; - - if (sat === 0) { - rgb[0] = val; - rgb[1] = val; - rgb[2] = val; - return rgb; - } - - var hueCase = Math.floor(hue * 6); - var frac = 6 * hue - hueCase; - var lx = val * (1 - sat); - var ly = val * (1 - sat * frac); - var lz = val * (1 - sat * (1 - frac)); - - switch (hueCase) { - /* 0 1) && !force) { - return; - } - - // Clear the table - this.Table = []; - - var maxIndex = this.NumberOfColors - 1; - - var hinc, sinc, vinc, ainc; - if (maxIndex) { - hinc = (this.HueRange[1] - this.HueRange[0]) / maxIndex; - sinc = (this.SaturationRange[1] - this.SaturationRange[0]) / maxIndex; - vinc = (this.ValueRange[1] - this.ValueRange[0]) / maxIndex; - ainc = (this.AlphaRange[1] - this.AlphaRange[0]) / maxIndex; - } else { - hinc = sinc = vinc = ainc = 0.0; - } - - for (var i = 0; i <= maxIndex; i++) { - var hue = this.HueRange[0] + i * hinc; - var sat = this.SaturationRange[0] + i * sinc; - var val = this.ValueRange[0] + i * vinc; - var alpha = this.AlphaRange[0] + i * ainc; - - var rgb = this.HSVToRGB(hue, sat, val); - var c_rgba = []; - - switch(this.Ramp) { - case 'scurve': - c_rgba[0] = Math.floor(127.5 * (1.0 + Math.cos((1.0 - rgb[0]) * Math.PI))); - c_rgba[1] = Math.floor(127.5 * (1.0 + Math.cos((1.0 - rgb[1]) * Math.PI))); - c_rgba[2] = Math.floor(127.5 * (1.0 + Math.cos((1.0 - rgb[2]) * Math.PI))); - c_rgba[3] = Math.floor(alpha * 255); - break; - case 'linear': - c_rgba[0] = Math.floor(rgb[0] * 255 + 0.5); - c_rgba[1] = Math.floor(rgb[1] * 255 + 0.5); - c_rgba[2] = Math.floor(rgb[2] * 255 + 0.5); - c_rgba[3] = Math.floor(alpha * 255 + 0.5); - break; - case 'sqrt': - c_rgba[0] = Math.floor(Math.sqrt(rgb[0]) * 255 + 0.5); - c_rgba[1] = Math.floor(Math.sqrt(rgb[1]) * 255 + 0.5); - c_rgba[2] = Math.floor(Math.sqrt(rgb[2]) * 255 + 0.5); - c_rgba[3] = Math.floor(Math.sqrt(alpha) * 255 + 0.5); - break; - default: - throw new Error('Invalid Ramp value (' + this.Ramp + ')'); - } - - this.Table.push(c_rgba); - } - - this.buildSpecialColors(); - }; - - this.buildSpecialColors = function() { - var numberOfColors = this.NumberOfColors; - var belowRangeColorIndex = numberOfColors + BELOW_RANGE_COLOR_INDEX; - var aboveRangeColorIndex = numberOfColors + ABOVE_RANGE_COLOR_INDEX; - var nanColorIndex = numberOfColors + NAN_COLOR_INDEX; - - // Below range color - if (this.UseBelowRangeColor || numberOfColors === 0) { - this.Table[belowRangeColorIndex] = this.BelowRangeColor; - } else { - // Duplicate the first color in the table. - this.Table[belowRangeColorIndex] = this.Table[0]; - } - - // Above range color - if (this.UseAboveRangeColor || numberOfColors === 0) { - this.Table[aboveRangeColorIndex] = this.AboveRangeColor; - } else { - // Duplicate the last color in the table. - this.Table[aboveRangeColorIndex] = this.Table[numberOfColors - 1]; - } - - // Always use NanColor - this.Table[nanColorIndex] = this.NaNColor; - }; - - // Given a scalar value v, return an rgba color value from lookup table. - this.mapValue = function(v) { - var index = this.getIndex(v); - if (index < 0) { - return this.NaNColor; - } else if (index === 0) { - if (this.UseBelowRangeColor && v < this.TableRange[0]) { - return this.BelowRangeColor; - } - } else if (index === this.NumberOfColors - 1) { - if (this.UseAboveRangeColor && v > this.TableRange[1]) { - return this.AboveRangeColor; - } - } - - return this.Table[index]; - }; - - this.linearIndexLookupMain = function(v, p) { - var dIndex; - - // NOTE: Added Math.floor since values were not integers? Check VTK source - if (v < p.Range[0]) { - dIndex = p.MaxIndex + BELOW_RANGE_COLOR_INDEX + 1.5; - } else if (v > p.Range[1]) { - dIndex = p.MaxIndex + ABOVE_RANGE_COLOR_INDEX + 1.5; - } else { - dIndex = (v + p.Shift) * p.Scale; - } - - return Math.round(dIndex); - }; - - this.getIndex = function(v) { - var p = {}; - p.Range = []; - p.MaxIndex = this.NumberOfColors - 1; - - // This was LookupShiftAndScale - p.Shift = -this.TableRange[0]; - if (this.TableRange[1] <= this.TableRange[0]) { - p.Scale = Number.MAX_VALUE; - } else { - p.Scale = p.MaxIndex / (this.TableRange[1] - this.TableRange[0]); - } - - p.Range[0] = this.TableRange[0]; - p.Range[1] = this.TableRange[1]; - - // First, check whether we have a number... - if (isNaN(v)) { - // For backwards compatibility - return -1; - } - - // Map to an index: - var index = this.linearIndexLookupMain(v, p); - - // For backwards compatibility, if the index indicates an - // out-of-range value, truncate to index range for in-range colors. - if (index === this.NumberOfColors + BELOW_RANGE_COLOR_INDEX) { - index = 0; - } else if (index === this.NumberOfColors + ABOVE_RANGE_COLOR_INDEX) { - index = this.NumberOfColors - 1; - } - - return index; - }; - - this.setTableValue = function(index, rgba) { - // Check if it index, red, green, blue and alpha were passed as parameter - if(arguments.length === 5) { - rgba = Array.prototype.slice.call(arguments, 1); - } - - // Check the index to make sure it is valid - if (index < 0) { - throw new Error('Can\'t set the table value for negative index (' + index + ')'); - } - - if (index >= this.NumberOfColors) { - new Error('Index ' + index + ' is greater than the number of colors ' + this.NumberOfColors); - } - - this.Table[index] = rgba; - - if ((index === 0) || (index === this.NumberOfColors - 1)) { - // This is needed due to the way the special colors are stored in - // the internal table. If Above/BelowRangeColors are not used and - // the min/max colors are changed in the table with this member - // function, then the colors used for values outside the range may - // be incorrect. Calling this here ensures the out-of-range colors - // are set correctly. - this.buildSpecialColors(); - } - }; - } - - // module exports - cornerstone.colors.LookupTable = LookupTable; - -}(cornerstone)); -(function (cornerstone) { - - "use strict"; - - function disable(element) { - if(element === undefined) { - throw "disable: element element must not be undefined"; - } - - // Search for this element in this list of enabled elements - var enabledElements = cornerstone.getEnabledElements(); - for(var i=0; i < enabledElements.length; i++) { - if(enabledElements[i].element === element) { - // We found it! - - // Fire an event so dependencies can cleanup - var eventData = { - element : element - }; - $(element).trigger("CornerstoneElementDisabled", eventData); - - // remove the child dom elements that we created (e.g.canvas) - enabledElements[i].element.removeChild(enabledElements[i].canvas); - enabledElements[i].canvas = undefined; - - // remove this element from the list of enabled elements - enabledElements.splice(i, 1); - return; - } - } - } - - // module/private exports - cornerstone.disable = disable; - -}(cornerstone)); -/** - * This module is responsible for enabling an element to display images with cornerstone - */ -(function ($, cornerstone) { - - "use strict"; - - /** - * sets a new image object for a given element - * @param element - * @param image - */ - function displayImage(element, image, viewport) { - if(element === undefined) { - throw "displayImage: parameter element cannot be undefined"; - } - if(image === undefined) { - throw "displayImage: parameter image cannot be undefined"; - } - - var enabledElement = cornerstone.getEnabledElement(element); - - enabledElement.image = image; - - if(enabledElement.viewport === undefined) { - enabledElement.viewport = cornerstone.internal.getDefaultViewport(enabledElement.canvas, image); - } - - // merge viewport - if(viewport) { - for(var attrname in viewport) - { - if(viewport[attrname] !== null) { - enabledElement.viewport[attrname] = viewport[attrname]; - } - } - } - - var now = new Date(); - var frameRate; - if(enabledElement.lastImageTimeStamp !== undefined) { - var timeSinceLastImage = now.getTime() - enabledElement.lastImageTimeStamp; - frameRate = (1000 / timeSinceLastImage).toFixed(); - } else { - } - enabledElement.lastImageTimeStamp = now.getTime(); - - var newImageEventData = { - viewport : enabledElement.viewport, - element : enabledElement.element, - image : enabledElement.image, - enabledElement : enabledElement, - frameRate : frameRate - }; - - $(enabledElement.element).trigger("CornerstoneNewImage", newImageEventData); - - cornerstone.updateImage(element); - } - - // module/private exports - cornerstone.displayImage = displayImage; -}($, cornerstone)); -/** - * This module is responsible for immediately drawing an enabled element - */ - -(function ($, cornerstone) { - - "use strict"; - - /** - * Immediately draws the enabled element - * - * @param element - */ - function draw(element) { - var enabledElement = cornerstone.getEnabledElement(element); - - if(enabledElement.image === undefined) { - throw "draw: image has not been loaded yet"; - } - - cornerstone.drawImage(enabledElement); - } - - // Module exports - cornerstone.draw = draw; - -}($, cornerstone)); -/** - * This module is responsible for drawing invalidated enabled elements - */ - -(function ($, cornerstone) { - - "use strict"; - - /** - * Draws all invalidated enabled elements and clears the invalid flag after drawing it - */ - function drawInvalidated() - { - var enabledElements = cornerstone.getEnabledElements(); - for(var i=0;i < enabledElements.length; i++) { - var ee = enabledElements[i]; - if(ee.invalid === true) { - cornerstone.drawImage(ee, true); - } - } - } - - // Module exports - cornerstone.drawInvalidated = drawInvalidated; -}($, cornerstone)); - -/** - * This module is responsible for enabling an element to display images with cornerstone - */ -(function (cornerstone) { - - "use strict"; - - function enable(element, options) { - if(element === undefined) { - throw "enable: parameter element cannot be undefined"; - } - - // If this enabled element has the option set for WebGL, we should - // check if this device actually supports it - if (options && options.renderer && options.renderer.toLowerCase() === 'webgl') { - if (cornerstone.webGL.renderer.isWebGLAvailable()) { - // If WebGL is available on the device, initialize the renderer - // and return the renderCanvas from the WebGL rendering path - console.log('Using WebGL rendering path'); - - cornerstone.webGL.renderer.initRenderer(); - options.renderer = 'webgl'; - } else { - // If WebGL is not available on this device, we will fall back - // to using the Canvas renderer - console.error('WebGL not available, falling back to Canvas renderer'); - delete options.renderer; - } - } - - var canvas = document.createElement('canvas'); - element.appendChild(canvas); - - var el = { - element: element, - canvas: canvas, - image : undefined, // will be set once image is loaded - invalid: false, // true if image needs to be drawn, false if not - needsRedraw:true, - options: options, - data : {} - }; - cornerstone.addEnabledElement(el); - - cornerstone.resize(element, true); - - - function draw() { - if (el.canvas === undefined){ - return; - } - if (el.needsRedraw && el.image !== undefined){ - var start = new Date(); - var render = el.image.render; - - if(!render) { - render = el.image.color ? cornerstone.renderColorImage : cornerstone.renderGrayscaleImage; - } - - render(el, el.invalid); - - var context = el.canvas.getContext('2d'); - - var end = new Date(); - var diff = end - start; - - var eventData = { - viewport: el.viewport, - element: el.element, - image: el.image, - enabledElement: el, - canvasContext: context, - renderTimeInMs: diff - }; - - el.invalid = false; - el.needsRedraw = false; - $(el.element).trigger("CornerstoneImageRendered", eventData); - } - - cornerstone.requestAnimationFrame(draw); - } - - draw(); - - return element; - } - - // module/private exports - cornerstone.enable = enable; -}(cornerstone)); - -(function (cornerstone) { - - "use strict"; - - function getElementData(el, dataType) { - var ee = cornerstone.getEnabledElement(el); - if(ee.data.hasOwnProperty(dataType) === false) - { - ee.data[dataType] = {}; - } - return ee.data[dataType]; - } - - function removeElementData(el, dataType) { - var ee = cornerstone.getEnabledElement(el); - delete ee.data[dataType]; - } - - // module/private exports - cornerstone.getElementData = getElementData; - cornerstone.removeElementData = removeElementData; - -}(cornerstone)); -(function (cornerstone) { - - "use strict"; - - var enabledElements = []; - - function getEnabledElement(element) { - if(element === undefined) { - throw "getEnabledElement: parameter element must not be undefined"; - } - for(var i=0; i < enabledElements.length; i++) { - if(enabledElements[i].element == element) { - return enabledElements[i]; - } - } - - throw "element not enabled"; - } - - function addEnabledElement(enabledElement) { - if(enabledElement === undefined) { - throw "getEnabledElement: enabledElement element must not be undefined"; - } - - enabledElements.push(enabledElement); - } - - function getEnabledElementsByImageId(imageId) { - var ees = []; - enabledElements.forEach(function(enabledElement) { - if(enabledElement.image && enabledElement.image.imageId === imageId) { - ees.push(enabledElement); - } - }); - return ees; - } - - function getEnabledElements() { - return enabledElements; - } - - // module/private exports - cornerstone.getEnabledElement = getEnabledElement; - cornerstone.addEnabledElement = addEnabledElement; - cornerstone.getEnabledElementsByImageId = getEnabledElementsByImageId; - cornerstone.getEnabledElements = getEnabledElements; -}(cornerstone)); -(function(cornerstone) { - - "use strict"; - - function getPixelValues(pixelData) { - var minPixelValue = Number.MAX_VALUE; - var maxPixelValue = Number.MIN_VALUE; - var len = pixelData.length; - var pixel; - - for(var i = 0; i < len; i++) { - pixel = pixelData[i]; - minPixelValue = minPixelValue < pixel ? minPixelValue : pixel; - maxPixelValue = maxPixelValue > pixel ? maxPixelValue : pixel; - } - - return { - minPixelValue: minPixelValue, - maxPixelValue: maxPixelValue - }; - } - - function getRestoreImageMethod(image) { - if(image.restore) { - return image.restore; - } - - var color = image.color; - var rgba = image.rgba; - var lut = image.lut; - var slope = image.slope; - var windowWidth = image.windowWidth; - var windowCenter = image.windowCenter; - var minPixelValue = image.minPixelValue; - var maxPixelValue = image.maxPixelValue; - - return function() { - image.color = color; - image.rgba = rgba; - image.lut = lut; - image.slope = slope; - image.windowWidth = windowWidth; - image.windowCenter = windowCenter; - image.minPixelValue = minPixelValue; - image.maxPixelValue = maxPixelValue; - - if(image.origPixelData) { - var pixelData = image.origPixelData; - image.getPixelData = function() { - return pixelData; - }; - } - - // Remove some attributes added by false color mapping - delete image.origPixelData; - delete image.colormapId; - delete image.falseColor; - }; - } - - // User can pass a colormap or its id as string to some of these public functions. - // Then we need to make sure it will be converted into a colormap object if it's as string. - function ensuresColormap(colormap) { - if(colormap && (typeof colormap === 'string')) { - colormap = cornerstone.colors.getColormap(colormap); - } - - return colormap; - } - - /** - * Restores a false color image to its original version - * @param image - */ - function restoreImage(image) { - if(image.restore && (typeof image.restore === 'function')) { - image.restore(); - return true; - } - - return false; - } - - /** - * Convert an image to a false color image - * @param image - * @param colormap - it can be a colormap object or a colormap id (string) - */ - function convertImageToFalseColorImage(image, colormap) { - if (image.color && !image.falseColor) { - throw "Color transforms are not implemented yet"; - } - - // User can pass a colormap id or a colormap object - colormap = ensuresColormap(colormap); - - var colormapId = colormap.getId(); - - // Doesn't do anything if colormapId hasn't changed - if(image.colormapId === colormapId) { - // It has already being converted into a false color image - // using the colormapId passed as parameter - return false; - } - - // Restore the image attributes updated when converting to a false color image - restoreImage(image); - - // Convert the image to a false color image - if(colormapId) { - var minPixelValue = image.minPixelValue || 0; - var maxPixelValue = image.maxPixelValue || 255; - var lookupTable; - - image.restore = getRestoreImageMethod(image); - // cacheOriginalImageAttrs(image); - - // var colormap = cornerstone.colors.getColormap(colormapId); - lookupTable = colormap.createLookupTable(); - lookupTable.setTableRange(minPixelValue, maxPixelValue); - - // Update the pixel data and render the new image - cornerstone.pixelDataToFalseColorData(image, lookupTable); - - // Update min and max pixel values - var pixelValues = getPixelValues(image.getPixelData()); - image.minPixelValue = pixelValues.minPixelValue; - image.maxPixelValue = pixelValues.maxPixelValue; - - // Cache the last colormapId used for performance - // then it doesn't need to be re-rendered on next - // time if the user hasn't updated it - image.colormapId = colormapId; - } - - // Return `true` to tell the caller that the image has got updated - return true; - } - - /** - * Convert the image of a element to a false color image - * @param element - * @param colormap - it can be a colormap object or a colormap id (string) - */ - function convertToFalseColorImage(element, colormap) { - var enabledElement = cornerstone.getEnabledElement(element); - var falseColorImageUpdated = convertImageToFalseColorImage(enabledElement.image, colormap); - - if(falseColorImageUpdated) { - cornerstone.updateImage(element, true); - } - } - - cornerstone.convertImageToFalseColorImage = convertImageToFalseColorImage; - cornerstone.convertToFalseColorImage = convertToFalseColorImage; - cornerstone.restoreImage = restoreImage; -}(cornerstone)); - -/** - * This module will fit an image to fit inside the canvas displaying it such that all pixels - * in the image are viewable - */ -(function (cornerstone) { - - "use strict"; - - function getImageSize(enabledElement) { - if(enabledElement.viewport.rotation === 0 ||enabledElement.viewport.rotation === 180) { - return { - width: enabledElement.image.width, - height: enabledElement.image.height - }; - } else { - return { - width: enabledElement.image.height, - height: enabledElement.image.width - }; - } - } - - /** - * Adjusts an images scale and center so the image is centered and completely visible - * @param element - */ - function fitToWindow(element) - { - var enabledElement = cornerstone.getEnabledElement(element); - var imageSize = getImageSize(enabledElement); - - var verticalScale = enabledElement.canvas.height / imageSize.height; - var horizontalScale= enabledElement.canvas.width / imageSize.width; - if(horizontalScale < verticalScale) { - enabledElement.viewport.scale = horizontalScale; - } - else - { - enabledElement.viewport.scale = verticalScale; - } - enabledElement.viewport.translation.x = 0; - enabledElement.viewport.translation.y = 0; - cornerstone.updateImage(element); - } - - cornerstone.fitToWindow = fitToWindow; -}(cornerstone)); - -/** - * This file is responsible for returning the default viewport for an image - */ - -(function ($, cornerstone) { - - "use strict"; - - /** - * returns a default viewport for display the specified image on the specified - * enabled element. The default viewport is fit to window - * - * @param element - * @param image - */ - function getDefaultViewportForImage(element, image) { - var enabledElement = cornerstone.getEnabledElement(element); - var viewport = cornerstone.internal.getDefaultViewport(enabledElement.canvas, image); - return viewport; - } - - // Module exports - cornerstone.getDefaultViewportForImage = getDefaultViewportForImage; -}($, cornerstone)); -/** - * This module is responsible for returning the currently displayed image for an element - */ - -(function ($, cornerstone) { - - "use strict"; - - /** - * returns the currently displayed image for an element or undefined if no image has - * been displayed yet - * - * @param element - */ - function getImage(element) { - var enabledElement = cornerstone.getEnabledElement(element); - return enabledElement.image; - } - - // Module exports - cornerstone.getImage = getImage; -}($, cornerstone)); -/** - * This module returns a subset of the stored pixels of an image - */ -(function (cornerstone) { - - "use strict"; - - /** - * Returns array of pixels with modality LUT transformation applied - */ - function getPixels(element, x, y, width, height) { - - var storedPixels = cornerstone.getStoredPixels(element, x, y, width, height); - var ee = cornerstone.getEnabledElement(element); - - var mlutfn = cornerstone.internal.getModalityLUT(ee.image.slope, ee.image.intercept, ee.viewport.modalityLUT); - - var modalityPixels = storedPixels.map(mlutfn); - - return modalityPixels; - } - - // module exports - cornerstone.getPixels = getPixels; -}(cornerstone)); -/** - * This module returns a subset of the stored pixels of an image - */ -(function (cornerstone) { - - "use strict"; - - /** - * Returns an array of stored pixels given a rectangle in the image - * @param element - * @param x - * @param y - * @param width - * @param height - * @returns {Array} - */ - function getStoredPixels(element, x, y, width, height) { - if(element === undefined) { - throw "getStoredPixels: parameter element must not be undefined"; - } - - x = Math.round(x); - y = Math.round(y); - var ee = cornerstone.getEnabledElement(element); - var storedPixels = []; - var index = 0; - var pixelData = ee.image.getPixelData(); - for(var row=0; row < height; row++) { - for(var column=0; column < width; column++) { - var spIndex = ((row + y) * ee.image.columns) + (column + x); - storedPixels[index++] = pixelData[spIndex]; - } - } - return storedPixels; - } - - // module exports - cornerstone.getStoredPixels = getStoredPixels; -}(cornerstone)); -/** - * This module contains functions to deal with getting and setting the viewport for an enabled element - */ -(function (cornerstone) { - - "use strict"; - - /** - * Returns the viewport for the specified enabled element - * @param element - * @returns {*} - */ - function getViewport(element) { - var enabledElement = cornerstone.getEnabledElement(element); - - var viewport = enabledElement.viewport; - if(viewport === undefined) { - return undefined; - } - return { - scale : viewport.scale, - translation : { - x : viewport.translation.x, - y : viewport.translation.y - }, - voi : { - windowWidth: viewport.voi.windowWidth, - windowCenter : viewport.voi.windowCenter - }, - invert : viewport.invert, - pixelReplication: viewport.pixelReplication, - rotation: viewport.rotation, - hflip: viewport.hflip, - vflip: viewport.vflip, - modalityLUT: viewport.modalityLUT, - voiLUT: viewport.voiLUT - }; - } - - // module/private exports - cornerstone.getViewport = getViewport; - -}(cornerstone)); - -/** - * This module deals with caching images - */ - -(function (cornerstone) { - - "use strict"; - - // dictionary of imageId to cachedImage objects - var imageCache = {}; - // array of cachedImage objects - var cachedImages = []; - - var maximumSizeInBytes = 1024 * 1024 * 1024; // 1 GB - var cacheSizeInBytes = 0; - - function setMaximumSizeBytes(numBytes) { - if (numBytes === undefined) { - throw "setMaximumSizeBytes: parameter numBytes must not be undefined"; - } - if (numBytes.toFixed === undefined) { - throw "setMaximumSizeBytes: parameter numBytes must be a number"; - } - - maximumSizeInBytes = numBytes; - purgeCacheIfNecessary(); - } - - function purgeCacheIfNecessary() { - // if max cache size has not been exceeded, do nothing - if (cacheSizeInBytes <= maximumSizeInBytes) { - return; - } - - // cache size has been exceeded, create list of images sorted by timeStamp - // so we can purge the least recently used image - function compare(a,b) { - if (a.timeStamp > b.timeStamp) { - return -1; - } - if (a.timeStamp < b.timeStamp) { - return 1; - } - return 0; - } - cachedImages.sort(compare); - - // remove images as necessary - while(cacheSizeInBytes > maximumSizeInBytes) { - var lastCachedImage = cachedImages[cachedImages.length - 1]; - var imageId = lastCachedImage.imageId; - - removeImagePromise(imageId); - - $(cornerstone).trigger('CornerstoneImageCachePromiseRemoved', {imageId: imageId}); - } - - var cacheInfo = cornerstone.imageCache.getCacheInfo(); - $(cornerstone).trigger('CornerstoneImageCacheFull', cacheInfo); - } - - function putImagePromise(imageId, imagePromise) { - if (imageId === undefined) { - throw "getImagePromise: imageId must not be undefined"; - } - if (imagePromise === undefined) { - throw "getImagePromise: imagePromise must not be undefined"; - } - - if (imageCache.hasOwnProperty(imageId) === true) { - throw "putImagePromise: imageId already in cache"; - } - - var cachedImage = { - loaded : false, - imageId : imageId, - sharedCacheKey: undefined, // the sharedCacheKey for this imageId. undefined by default - imagePromise : imagePromise, - timeStamp : new Date(), - sizeInBytes: 0 - }; - - imageCache[imageId] = cachedImage; - cachedImages.push(cachedImage); - - imagePromise.then(function(image) { - cachedImage.loaded = true; - cachedImage.image = image; - - if (image.sizeInBytes === undefined) { - throw "putImagePromise: image does not have sizeInBytes property or"; - } - if (image.sizeInBytes.toFixed === undefined) { - throw "putImagePromise: image.sizeInBytes is not a number"; - } - - cachedImage.sizeInBytes = image.sizeInBytes; - cacheSizeInBytes += cachedImage.sizeInBytes; - cachedImage.sharedCacheKey = image.sharedCacheKey; - - purgeCacheIfNecessary(); - }); - } - - function getImagePromise(imageId) { - if (imageId === undefined) { - throw "getImagePromise: imageId must not be undefined"; - } - var cachedImage = imageCache[imageId]; - if (cachedImage === undefined) { - return undefined; - } - - // bump time stamp for cached image - cachedImage.timeStamp = new Date(); - return cachedImage.imagePromise; - } - - function removeImagePromise(imageId) { - if (imageId === undefined) { - throw "removeImagePromise: imageId must not be undefined"; - } - var cachedImage = imageCache[imageId]; - if (cachedImage === undefined) { - throw "removeImagePromise: imageId must not be undefined"; - } - - cachedImage.imagePromise.reject(); - cachedImages.splice( cachedImages.indexOf(cachedImage), 1); - cacheSizeInBytes -= cachedImage.sizeInBytes; - decache(cachedImage.imagePromise, cachedImage.imageId); - - delete imageCache[imageId]; - - return cachedImage.imagePromise; - } - - function getCacheInfo() { - return { - maximumSizeInBytes : maximumSizeInBytes, - cacheSizeInBytes : cacheSizeInBytes, - numberOfImagesCached: cachedImages.length - }; - } - - // This method should only be called by `removeImagePromise` because it's - // the one that knows how to deal with shared cache keys and cache size. - function decache(imagePromise, imageId) { - imagePromise.then(function(image) { - if(image.decache) { - image.decache(); - } - }).always(function() { - delete imageCache[imageId]; - }); - } - - function purgeCache() { - while (cachedImages.length > 0) { - var removedCachedImage = cachedImages[0]; - removeImagePromise(removedCachedImage.imageId); - } - } - - function changeImageIdCacheSize(imageId, newCacheSize) { - var cacheEntry = imageCache[imageId]; - if(cacheEntry) { - cacheEntry.imagePromise.then(function(image) { - var cacheSizeDifference = newCacheSize - image.sizeInBytes; - image.sizeInBytes = newCacheSize; - cacheSizeInBytes += cacheSizeDifference; - }); - } - } - - // module exports - cornerstone.imageCache = { - putImagePromise : putImagePromise, - getImagePromise: getImagePromise, - removeImagePromise: removeImagePromise, - setMaximumSizeBytes: setMaximumSizeBytes, - getCacheInfo : getCacheInfo, - purgeCache: purgeCache, - cachedImages: cachedImages, - imageCache: imageCache, - changeImageIdCacheSize: changeImageIdCacheSize - }; - -}(cornerstone)); - -/** - * This module deals with ImageLoaders, loading images and caching images - */ - -(function ($, cornerstone) { - - "use strict"; - - var imageLoaders = {}; - - var unknownImageLoader; - - function loadImageFromImageLoader(imageId, options) { - var colonIndex = imageId.indexOf(":"); - var scheme = imageId.substring(0, colonIndex); - var loader = imageLoaders[scheme]; - var imagePromise; - if(loader === undefined || loader === null) { - if(unknownImageLoader !== undefined) { - imagePromise = unknownImageLoader(imageId); - return imagePromise; - } - else { - return undefined; - } - } - imagePromise = loader(imageId, options); - - // broadcast an image loaded event once the image is loaded - // This is based on the idea here: http://stackoverflow.com/questions/3279809/global-custom-events-in-jquery - imagePromise.then(function(image) { - $(cornerstone).trigger('CornerstoneImageLoaded', {image: image}); - }); - - return imagePromise; - } - - // Loads an image given an imageId and optional priority and returns a promise which will resolve - // to the loaded image object or fail if an error occurred. The loaded image - // is not stored in the cache - function loadImage(imageId, options) { - if(imageId === undefined) { - throw "loadImage: parameter imageId must not be undefined"; - } - - var imagePromise = cornerstone.imageCache.getImagePromise(imageId); - if(imagePromise !== undefined) { - return imagePromise; - } - - imagePromise = loadImageFromImageLoader(imageId, options); - if(imagePromise === undefined) { - throw "loadImage: no image loader for imageId"; - } - - return imagePromise; - } - - // Loads an image given an imageId and optional priority and returns a promise which will resolve - // to the loaded image object or fail if an error occurred. The image is - // stored in the cache - function loadAndCacheImage(imageId, options) { - if(imageId === undefined) { - throw "loadAndCacheImage: parameter imageId must not be undefined"; - } - - var imagePromise = cornerstone.imageCache.getImagePromise(imageId); - if(imagePromise !== undefined) { - return imagePromise; - } - - imagePromise = loadImageFromImageLoader(imageId, options); - if(imagePromise === undefined) { - throw "loadAndCacheImage: no image loader for imageId"; - } - - cornerstone.imageCache.putImagePromise(imageId, imagePromise); - - return imagePromise; - } - - - // registers an imageLoader plugin with cornerstone for the specified scheme - function registerImageLoader(scheme, imageLoader) { - imageLoaders[scheme] = imageLoader; - } - - // Registers a new unknownImageLoader and returns the previous one (if it exists) - function registerUnknownImageLoader(imageLoader) { - var oldImageLoader = unknownImageLoader; - unknownImageLoader = imageLoader; - return oldImageLoader; - } - - // module exports - - cornerstone.loadImage = loadImage; - cornerstone.loadAndCacheImage = loadAndCacheImage; - cornerstone.registerImageLoader = registerImageLoader; - cornerstone.registerUnknownImageLoader = registerUnknownImageLoader; - -}($, cornerstone)); - -(function (cornerstone) { - - "use strict"; - - function calculateTransform(enabledElement, scale) { - - var transform = new cornerstone.internal.Transform(); - transform.translate(enabledElement.canvas.width/2, enabledElement.canvas.height / 2); - - //Apply the rotation before scaling for non square pixels - var angle = enabledElement.viewport.rotation; - if(angle!==0) { - transform.rotate(angle*Math.PI/180); - } - - // apply the scale - var widthScale = enabledElement.viewport.scale; - var heightScale = enabledElement.viewport.scale; - if(enabledElement.image.rowPixelSpacing < enabledElement.image.columnPixelSpacing) { - widthScale = widthScale * (enabledElement.image.columnPixelSpacing / enabledElement.image.rowPixelSpacing); - } - else if(enabledElement.image.columnPixelSpacing < enabledElement.image.rowPixelSpacing) { - heightScale = heightScale * (enabledElement.image.rowPixelSpacing / enabledElement.image.columnPixelSpacing); - } - transform.scale(widthScale, heightScale); - - // unrotate to so we can translate unrotated - if(angle!==0) { - transform.rotate(-angle*Math.PI/180); - } - - // apply the pan offset - transform.translate(enabledElement.viewport.translation.x, enabledElement.viewport.translation.y); - - // rotate again so we can apply general scale - if(angle!==0) { - transform.rotate(angle*Math.PI/180); - } - - if(scale !== undefined) { - // apply the font scale - transform.scale(scale, scale); - } - - //Apply Flip if required - if(enabledElement.viewport.hflip) { - transform.scale(-1,1); - } - - if(enabledElement.viewport.vflip) { - transform.scale(1,-1); - } - - // translate the origin back to the corner of the image so the event handlers can draw in image coordinate system - transform.translate(-enabledElement.image.width / 2 , -enabledElement.image.height/ 2); - return transform; - } - - // Module exports - cornerstone.internal.calculateTransform = calculateTransform; -}(cornerstone)); -/** - * This module is responsible for drawing an image to an enabled elements canvas element - */ - -(function ($, cornerstone) { - - "use strict"; - - /** - * Internal API function to draw an image to a given enabled element - * @param enabledElement - * @param invalidated - true if pixel data has been invalidated and cached rendering should not be used - */ - function drawImage(enabledElement, invalidated) { - enabledElement.needsRedraw = true; - if (invalidated){ - enabledElement.invalid = true; - } - - } - - // Module exports - cornerstone.internal.drawImage = drawImage; - cornerstone.drawImage = drawImage; - -}($, cornerstone)); -/** - * This module generates a lut for an image - */ - -(function (cornerstone) { - - "use strict"; - - function generateLutNew(image, windowWidth, windowCenter, invert, modalityLUT, voiLUT) - { - if(image.lut === undefined) { - image.lut = new Int16Array(image.maxPixelValue - Math.min(image.minPixelValue,0)+1); - } - var lut = image.lut; - var maxPixelValue = image.maxPixelValue; - var minPixelValue = image.minPixelValue; - - var mlutfn = cornerstone.internal.getModalityLUT(image.slope, image.intercept, modalityLUT); - var vlutfn = cornerstone.internal.getVOILUT(windowWidth, windowCenter, voiLUT); - - var offset = 0; - if(minPixelValue < 0) { - offset = minPixelValue; - } - var storedValue; - var modalityLutValue; - var voiLutValue; - var clampedValue; - - for(storedValue = image.minPixelValue; storedValue <= maxPixelValue; storedValue++) - { - modalityLutValue = mlutfn(storedValue); - voiLutValue = vlutfn(modalityLutValue); - clampedValue = Math.min(Math.max(voiLutValue, 0), 255); - if(!invert) { - lut[storedValue+ (-offset)] = Math.round(clampedValue); - } else { - lut[storedValue + (-offset)] = Math.round(255 - clampedValue); - } - } - return lut; - } - - - - /** - * Creates a LUT used while rendering to convert stored pixel values to - * display pixels - * - * @param image - * @returns {Array} - */ - function generateLut(image, windowWidth, windowCenter, invert, modalityLUT, voiLUT) - { - if(modalityLUT || voiLUT) { - return generateLutNew(image, windowWidth, windowCenter, invert, modalityLUT, voiLUT); - } - - if(image.lut === undefined) { - image.lut = new Int16Array(image.maxPixelValue - Math.min(image.minPixelValue,0)+1); - } - var lut = image.lut; - - var maxPixelValue = image.maxPixelValue; - var minPixelValue = image.minPixelValue; - var slope = image.slope; - var intercept = image.intercept; - var localWindowWidth = windowWidth; - var localWindowCenter = windowCenter; - var modalityLutValue; - var voiLutValue; - var clampedValue; - var storedValue; - - // NOTE: As of Nov 2014, most javascript engines have lower performance when indexing negative indexes. - // We improve performance by offsetting the pixel values for signed data to avoid negative indexes - // when generating the lut and then undo it in storedPixelDataToCanvasImagedata. Thanks to @jpambrun - // for this contribution! - - var offset = 0; - if(minPixelValue < 0) { - offset = minPixelValue; - } - - if(invert === true) { - for(storedValue = image.minPixelValue; storedValue <= maxPixelValue; storedValue++) - { - modalityLutValue = storedValue * slope + intercept; - voiLutValue = (((modalityLutValue - (localWindowCenter)) / (localWindowWidth) + 0.5) * 255.0); - clampedValue = Math.min(Math.max(voiLutValue, 0), 255); - lut[storedValue + (-offset)] = Math.round(255 - clampedValue); - } - } - else { - for(storedValue = image.minPixelValue; storedValue <= maxPixelValue; storedValue++) - { - modalityLutValue = storedValue * slope + intercept; - voiLutValue = (((modalityLutValue - (localWindowCenter)) / (localWindowWidth) + 0.5) * 255.0); - clampedValue = Math.min(Math.max(voiLutValue, 0), 255); - lut[storedValue+ (-offset)] = Math.round(clampedValue); - } - } - } - - - // Module exports - cornerstone.internal.generateLutNew = generateLutNew; - cornerstone.internal.generateLut = generateLut; - cornerstone.generateLutNew = generateLutNew; - cornerstone.generateLut = generateLut; -}(cornerstone)); - -/** - * This module contains a function to get a default viewport for an image given - * a canvas element to display it in - * - */ -(function (cornerstone) { - - "use strict"; - - /** - * Creates a new viewport object containing default values for the image and canvas - * @param canvas - * @param image - * @returns viewport object - */ - function getDefaultViewport(canvas, image) { - if(canvas === undefined) { - throw "getDefaultViewport: parameter canvas must not be undefined"; - } - if(image === undefined) { - throw "getDefaultViewport: parameter image must not be undefined"; - } - var viewport = { - scale : 1.0, - translation : { - x : 0, - y : 0 - }, - voi : { - windowWidth: image.windowWidth, - windowCenter: image.windowCenter, - }, - invert: image.invert, - pixelReplication: false, - rotation: 0, - hflip: false, - vflip: false, - modalityLUT: image.modalityLUT, - voiLUT: image.voiLUT - }; - - // fit image to window - var verticalScale = canvas.height / image.rows; - var horizontalScale= canvas.width / image.columns; - if(horizontalScale < verticalScale) { - viewport.scale = horizontalScale; - } - else { - viewport.scale = verticalScale; - } - return viewport; - } - - // module/private exports - cornerstone.internal.getDefaultViewport = getDefaultViewport; - cornerstone.getDefaultViewport = getDefaultViewport; -}(cornerstone)); - -(function (cornerstone) { - - "use strict"; - - function getTransform(enabledElement) - { - // For now we will calculate it every time it is requested. In the future, we may want to cache - // it in the enabled element to speed things up - var transform = cornerstone.internal.calculateTransform(enabledElement); - return transform; - } - - // Module exports - cornerstone.internal.getTransform = getTransform; - -}(cornerstone)); -/** - * This module is responsible for drawing an image to an enabled elements canvas element - */ - -(function ($, cornerstone) { - - "use strict"; - - cornerstone.drawImage = cornerstone.internal.drawImage; - cornerstone.generateLut = cornerstone.internal.generateLut; - cornerstone.storedPixelDataToCanvasImageData = cornerstone.internal.storedPixelDataToCanvasImageData; - cornerstone.storedColorPixelDataToCanvasImageData = cornerstone.internal.storedColorPixelDataToCanvasImageData; - -}($, cornerstone)); -/** - * This module generates a Modality LUT - */ - -(function (cornerstone) { - - "use strict"; - - - function generateLinearModalityLUT(slope, intercept) { - var localSlope = slope; - var localIntercept = intercept; - return function(sp) { - return sp * localSlope + localIntercept; - } - } - - function generateNonLinearModalityLUT(modalityLUT) { - var minValue = modalityLUT.lut[0]; - var maxValue = modalityLUT.lut[modalityLUT.lut.length -1]; - var maxValueMapped = modalityLUT.firstValueMapped + modalityLUT.lut.length; - return function(sp) { - if(sp < modalityLUT.firstValueMapped) { - return minValue; - } - else if(sp >= maxValueMapped) - { - return maxValue; - } - else - { - return modalityLUT.lut[sp]; - } - } - } - - function getModalityLUT(slope, intercept, modalityLUT) { - if (modalityLUT) { - return generateNonLinearModalityLUT(modalityLUT); - } else { - return generateLinearModalityLUT(slope, intercept); - } - } - - // Module exports - cornerstone.internal.getModalityLUT = getModalityLUT; - -}(cornerstone)); - -/** - * This module polyfills requestAnimationFrame for older browsers. - */ - - -(function (cornerstone) { - - 'use strict'; - - function requestFrame(callback) { - window.setTimeout(callback, 1000 / 60); - } - - function requestAnimationFrame(callback) { - return window.requestAnimationFrame(callback) || - window.webkitRequestAnimationFrame(callback) || - window.mozRequestAnimationFrame(callback) || - window.oRequestAnimationFrame(callback) || - window.msRequestAnimationFrame(callback) || - requestFrame(callback); - } - - // Module exports - cornerstone.requestAnimationFrame = requestAnimationFrame; - -})(cornerstone); -/** - * This module contains a function to convert stored pixel values to display pixel values using a LUT - */ -(function (cornerstone) { - - "use strict"; - - function storedColorPixelDataToCanvasImageData(image, lut, canvasImageDataData) - { - var minPixelValue = image.minPixelValue; - var canvasImageDataIndex = 0; - var storedPixelDataIndex = 0; - var numPixels = image.width * image.height * 4; - var storedPixelData = image.getPixelData(); - var localLut = lut; - var localCanvasImageDataData = canvasImageDataData; - - // NOTE: As of Nov 2014, most javascript engines have lower performance when indexing negative indexes. - // We have a special code path for this case that improves performance. Thanks to @jpambrun for this enhancement - if(minPixelValue < 0){ - while(storedPixelDataIndex < numPixels) { - localCanvasImageDataData[canvasImageDataIndex++] = localLut[storedPixelData[storedPixelDataIndex++] + (-minPixelValue)]; // red - localCanvasImageDataData[canvasImageDataIndex++] = localLut[storedPixelData[storedPixelDataIndex++] + (-minPixelValue)]; // green - localCanvasImageDataData[canvasImageDataIndex] = localLut[storedPixelData[storedPixelDataIndex] + (-minPixelValue)]; // blue - storedPixelDataIndex+=2; - canvasImageDataIndex+=2; - } - }else{ - while(storedPixelDataIndex < numPixels) { - localCanvasImageDataData[canvasImageDataIndex++] = localLut[storedPixelData[storedPixelDataIndex++]]; // red - localCanvasImageDataData[canvasImageDataIndex++] = localLut[storedPixelData[storedPixelDataIndex++]]; // green - localCanvasImageDataData[canvasImageDataIndex] = localLut[storedPixelData[storedPixelDataIndex]]; // blue - storedPixelDataIndex+=2; - canvasImageDataIndex+=2; - } - } - } - - // Module exports - cornerstone.internal.storedColorPixelDataToCanvasImageData = storedColorPixelDataToCanvasImageData; - cornerstone.storedColorPixelDataToCanvasImageData = storedColorPixelDataToCanvasImageData; - -}(cornerstone)); - -/** - * This module contains a function to convert stored pixel values to display pixel values using a LUT - */ -(function (cornerstone) { - - "use strict"; - - /** - * This function transforms stored pixel values into a canvas image data buffer - * by using a LUT. This is the most performance sensitive code in cornerstone and - * we use a special trick to make this go as fast as possible. Specifically we - * use the alpha channel only to control the luminance rather than the red, green and - * blue channels which makes it over 3x faster. The canvasImageDataData buffer needs - * to be previously filled with white pixels. - * - * NOTE: Attribution would be appreciated if you use this technique! - * - * @param pixelData the pixel data - * @param lut the lut - * @param canvasImageDataData a canvasImgageData.data buffer filled with white pixels - */ - function storedPixelDataToCanvasImageData(image, lut, canvasImageDataData) - { - var pixelData = image.getPixelData(); - var minPixelValue = image.minPixelValue; - var canvasImageDataIndex = 3; - var storedPixelDataIndex = 0; - var localNumPixels = pixelData.length; - var localPixelData = pixelData; - var localLut = lut; - var localCanvasImageDataData = canvasImageDataData; - - // NOTE: As of Nov 2014, most javascript engines have lower performance when indexing negative indexes. - // We have a special code path for this case that improves performance. Thanks to @jpambrun for this enhancement - if(minPixelValue < 0){ - while(storedPixelDataIndex < localNumPixels) { - localCanvasImageDataData[canvasImageDataIndex] = localLut[localPixelData[storedPixelDataIndex++] + (-minPixelValue)]; // alpha - canvasImageDataIndex += 4; - } - }else{ - while(storedPixelDataIndex < localNumPixels) { - localCanvasImageDataData[canvasImageDataIndex] = localLut[localPixelData[storedPixelDataIndex++]]; // alpha - canvasImageDataIndex += 4; - } - } - } - - // Module exports - cornerstone.internal.storedPixelDataToCanvasImageData = storedPixelDataToCanvasImageData; - cornerstone.storedPixelDataToCanvasImageData = storedPixelDataToCanvasImageData; - -}(cornerstone)); - -// Last updated November 2011 -// By Simon Sarris -// www.simonsarris.com -// sarris@acm.org -// -// Free to use and distribute at will -// So long as you are nice to people, etc - -// Simple class for keeping track of the current transformation matrix - -// For instance: -// var t = new Transform(); -// t.rotate(5); -// var m = t.m; -// ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]); - -// Is equivalent to: -// ctx.rotate(5); - -// But now you can retrieve it :) - -(function (cornerstone) { - - "use strict"; - - - // Remember that this does not account for any CSS transforms applied to the canvas - function Transform() { - this.reset(); - } - - Transform.prototype.reset = function() { - this.m = [1,0,0,1,0,0]; - }; - - Transform.prototype.clone = function() { - var transform = new Transform(); - transform.m[0] = this.m[0]; - transform.m[1] = this.m[1]; - transform.m[2] = this.m[2]; - transform.m[3] = this.m[3]; - transform.m[4] = this.m[4]; - transform.m[5] = this.m[5]; - return transform; - }; - - - Transform.prototype.multiply = function(matrix) { - var m11 = this.m[0] * matrix.m[0] + this.m[2] * matrix.m[1]; - var m12 = this.m[1] * matrix.m[0] + this.m[3] * matrix.m[1]; - - var m21 = this.m[0] * matrix.m[2] + this.m[2] * matrix.m[3]; - var m22 = this.m[1] * matrix.m[2] + this.m[3] * matrix.m[3]; - - var dx = this.m[0] * matrix.m[4] + this.m[2] * matrix.m[5] + this.m[4]; - var dy = this.m[1] * matrix.m[4] + this.m[3] * matrix.m[5] + this.m[5]; - - this.m[0] = m11; - this.m[1] = m12; - this.m[2] = m21; - this.m[3] = m22; - this.m[4] = dx; - this.m[5] = dy; - }; - - Transform.prototype.invert = function() { - var d = 1 / (this.m[0] * this.m[3] - this.m[1] * this.m[2]); - var m0 = this.m[3] * d; - var m1 = -this.m[1] * d; - var m2 = -this.m[2] * d; - var m3 = this.m[0] * d; - var m4 = d * (this.m[2] * this.m[5] - this.m[3] * this.m[4]); - var m5 = d * (this.m[1] * this.m[4] - this.m[0] * this.m[5]); - this.m[0] = m0; - this.m[1] = m1; - this.m[2] = m2; - this.m[3] = m3; - this.m[4] = m4; - this.m[5] = m5; - }; - - Transform.prototype.rotate = function(rad) { - var c = Math.cos(rad); - var s = Math.sin(rad); - var m11 = this.m[0] * c + this.m[2] * s; - var m12 = this.m[1] * c + this.m[3] * s; - var m21 = this.m[0] * -s + this.m[2] * c; - var m22 = this.m[1] * -s + this.m[3] * c; - this.m[0] = m11; - this.m[1] = m12; - this.m[2] = m21; - this.m[3] = m22; - }; - - Transform.prototype.translate = function(x, y) { - this.m[4] += this.m[0] * x + this.m[2] * y; - this.m[5] += this.m[1] * x + this.m[3] * y; - }; - - Transform.prototype.scale = function(sx, sy) { - this.m[0] *= sx; - this.m[1] *= sx; - this.m[2] *= sy; - this.m[3] *= sy; - }; - - Transform.prototype.transformPoint = function(px, py) { - var x = px; - var y = py; - px = x * this.m[0] + y * this.m[2] + this.m[4]; - py = x * this.m[1] + y * this.m[3] + this.m[5]; - return {x: px, y: py}; - }; - - cornerstone.internal.Transform = Transform; -}(cornerstone)); -/** - * This module generates a VOI LUT - */ - -(function (cornerstone) { - - "use strict"; - - function generateLinearVOILUT(windowWidth, windowCenter) { - return function(modalityLutValue) { - return (((modalityLutValue - (windowCenter)) / (windowWidth) + 0.5) * 255.0); - } - } - - function generateNonLinearVOILUT(voiLUT) { - var shift = voiLUT.numBitsPerEntry - 8; - var minValue = voiLUT.lut[0] >> shift; - var maxValue = voiLUT.lut[voiLUT.lut.length -1] >> shift; - var maxValueMapped = voiLUT.firstValueMapped + voiLUT.lut.length - 1; - return function(modalityLutValue) { - if(modalityLutValue < voiLUT.firstValueMapped) { - return minValue; - } - else if(modalityLutValue >= maxValueMapped) - { - return maxValue; - } - else - { - return voiLUT.lut[modalityLutValue - voiLUT.firstValueMapped] >> shift; - } - } - } - - function getVOILUT(windowWidth, windowCenter, voiLUT) { - if(voiLUT) { - return generateNonLinearVOILUT(voiLUT); - } else { - return generateLinearVOILUT(windowWidth, windowCenter); - } - } - - // Module exports - cornerstone.internal.getVOILUT = getVOILUT; -}(cornerstone)); - -/** - * This module contains a function to make an image is invalid - */ -(function (cornerstone) { - - "use strict"; - - /** - * Sets the invalid flag on the enabled element and fire an event - * @param element - */ - function invalidate(element) { - var enabledElement = cornerstone.getEnabledElement(element); - enabledElement.invalid = true; - enabledElement.needsRedraw = true; - var eventData = { - element: element - }; - $(enabledElement.element).trigger("CornerstoneInvalidated", eventData); - } - - // module exports - cornerstone.invalidate = invalidate; -}(cornerstone)); -/** - * This module contains a function to immediately invalidate an image - */ -(function (cornerstone) { - - "use strict"; - - /** - * Forces the image to be updated/redrawn for the specified enabled element - * @param element - */ - function invalidateImageId(imageId) { - - var enabledElements = cornerstone.getEnabledElementsByImageId(imageId); - enabledElements.forEach(function(enabledElement) { - cornerstone.drawImage(enabledElement, true); - }); - } - - // module exports - cornerstone.invalidateImageId = invalidateImageId; -}(cornerstone)); -(function($, cornerstone) { - - 'use strict'; - - // this module defines a way to access various metadata about an imageId. This layer of abstraction exists - // so metadata can be provided in different ways (e.g. by parsing DICOM P10 or by a WADO-RS document) - - var providers = []; - - /** - * Adds a metadata provider with the specified priority - * @param provider - * @param priority - 0 is default/normal, > 0 is high, < 0 is low - */ - function addProvider(provider, priority) { - priority = priority || 0; // default priority - // find the right spot to insert this provider based on priority - for(var i=0; i < providers.length; i++) { - if(providers[i].priority <= priority) { - break; - } - } - - // insert the decode task at position i - providers.splice(i, 0, { - priority: priority, - provider: provider - }); - - } - - /** - * Removes the specified provider - * @param provider - */ - function removeProvider( provider) { - for(var i=0; i < providers.length; i++) { - if(providers[i].provider === provider) { - providers.splice(i, 1); - return; - } - } - } - - /** - * Gets metadata from the registered metadata providers. Will call each one from highest priority to lowest - * until one responds - * - * @param type - * @param imageId - * @returns {boolean} - */ - function getMetaData(type, imageId) { - // invoke each provider in priority order until one returns something - for(var i=0; i < providers.length; i++) { - var result; - result = providers[i].provider(type, imageId); - if (result !== undefined) { - return result; - } - } - } - - // module/private exports - cornerstone.metaData = { - addProvider: addProvider, - removeProvider: removeProvider, - get: getMetaData - }; - -})($, cornerstone); - -/** - * This module contains a helper function to covert page coordinates to pixel coordinates - */ -(function (cornerstone) { - - "use strict"; - - /** - * Converts a point in the page coordinate system to the pixel coordinate - * system - * @param element - * @param pageX - * @param pageY - * @returns {{x: number, y: number}} - */ - function pageToPixel(element, pageX, pageY) { - var enabledElement = cornerstone.getEnabledElement(element); - - if(enabledElement.image === undefined) { - throw "image has not been loaded yet"; - } - - var image = enabledElement.image; - - // convert the pageX and pageY to the canvas client coordinates - var rect = element.getBoundingClientRect(); - var clientX = pageX - rect.left - window.pageXOffset; - var clientY = pageY - rect.top - window.pageYOffset; - - var pt = {x: clientX, y: clientY}; - var transform = cornerstone.internal.getTransform(enabledElement); - transform.invert(); - return transform.transformPoint(pt.x, pt.y); - } - - // module/private exports - cornerstone.pageToPixel = pageToPixel; - -}(cornerstone)); - -(function(cornerstone) { - - "use strict"; - - /** - * Converts the image pixel data into a false color data - * - * @param image - * @param lookupTable - */ - function pixelDataToFalseColorData(image, lookupTable) { - if (image.color && !image.falseColor) { - throw "Color transforms are not implemented yet"; - } - - var minPixelValue = image.minPixelValue; - var canvasImageDataIndex = 0; - var storedPixelDataIndex = 0; - var numPixels = image.width * image.height; - var origPixelData = image.origPixelData || image.getPixelData(); - var storedColorPixelData = new Uint8Array(numPixels * 4); - var localLookupTable = lookupTable; - var sp, mapped; - - image.color = true; - image.falseColor = true; - image.origPixelData = origPixelData; - - if (lookupTable instanceof cornerstone.colors.LookupTable) { - lookupTable.build(); - - while (storedPixelDataIndex < numPixels) { - sp = origPixelData[storedPixelDataIndex++]; - mapped = lookupTable.mapValue(sp); - storedColorPixelData[canvasImageDataIndex++] = mapped[0]; - storedColorPixelData[canvasImageDataIndex++] = mapped[1]; - storedColorPixelData[canvasImageDataIndex++] = mapped[2]; - storedColorPixelData[canvasImageDataIndex++] = mapped[3]; - } - } else { - if (minPixelValue < 0) { - while (storedPixelDataIndex < numPixels) { - sp = origPixelData[storedPixelDataIndex++]; - storedColorPixelData[canvasImageDataIndex++] = localLookupTable[sp + (-minPixelValue)][0]; // red - storedColorPixelData[canvasImageDataIndex++] = localLookupTable[sp + (-minPixelValue)][1]; // green - storedColorPixelData[canvasImageDataIndex++] = localLookupTable[sp + (-minPixelValue)][2]; // blue - storedColorPixelData[canvasImageDataIndex++] = localLookupTable[sp + (-minPixelValue)][3]; // alpha - } - } else { - while (storedPixelDataIndex < numPixels) { - sp = origPixelData[storedPixelDataIndex++]; - storedColorPixelData[canvasImageDataIndex++] = localLookupTable[sp][0]; // red - storedColorPixelData[canvasImageDataIndex++] = localLookupTable[sp][1]; // green - storedColorPixelData[canvasImageDataIndex++] = localLookupTable[sp][2]; // blue - storedColorPixelData[canvasImageDataIndex++] = localLookupTable[sp][3]; // alpha - } - } - - } - - image.rgba = true; - image.lut = undefined; - image.slope = 1; - image.minPixelValue = 0; - image.maxPixelValue = 255; - image.windowWidth = 255; - image.windowCenter = 128; - image.getPixelData = function() { - return storedColorPixelData; - }; - } - - cornerstone.pixelDataToFalseColorData = pixelDataToFalseColorData; - -}(cornerstone)); -(function (cornerstone) { - - "use strict"; - - /** - * Converts a point in the pixel coordinate system to the canvas coordinate system - * system. This can be used to render using canvas context without having the weird - * side effects that come from scaling and non square pixels - * @param element - * @param pt - * @returns {x: number, y: number} - */ - function pixelToCanvas(element, pt) { - var enabledElement = cornerstone.getEnabledElement(element); - var transform = cornerstone.internal.getTransform(enabledElement); - return transform.transformPoint(pt.x, pt.y); - } - - // module/private exports - cornerstone.pixelToCanvas = pixelToCanvas; - -}(cornerstone)); - -/** - * This module is responsible for drawing an image to an enabled elements canvas element - */ - -(function (cornerstone) { - - "use strict"; - - function initializeColorRenderCanvas(enabledElement, image) - { - var colorRenderCanvas = enabledElement.renderingTools.colorRenderCanvas; - // Resize the canvas - colorRenderCanvas.width = image.width; - colorRenderCanvas.height = image.height; - - // get the canvas data so we can write to it directly - var colorRenderCanvasContext = colorRenderCanvas.getContext('2d'); - colorRenderCanvasContext.fillStyle = 'white'; - colorRenderCanvasContext.fillRect(0,0, colorRenderCanvas.width, colorRenderCanvas.height); - var colorRenderCanvasData = colorRenderCanvasContext.getImageData(0,0,image.width, image.height); - - enabledElement.renderingTools.colorRenderCanvasContext = colorRenderCanvasContext; - enabledElement.renderingTools.colorRenderCanvasData = colorRenderCanvasData; - } - - - function getLut(image, viewport) - { - // if we have a cached lut and it has the right values, return it immediately - if(image.lut !== undefined && - image.lut.windowCenter === viewport.voi.windowCenter && - image.lut.windowWidth === viewport.voi.windowWidth && - image.lut.invert === viewport.invert) { - return image.lut; - } - - // lut is invalid or not present, regenerate it and cache it - cornerstone.generateLut(image, viewport.voi.windowWidth, viewport.voi.windowCenter, viewport.invert); - image.lut.windowWidth = viewport.voi.windowWidth; - image.lut.windowCenter = viewport.voi.windowCenter; - image.lut.invert = viewport.invert; - return image.lut; - } - - function doesImageNeedToBeRendered(enabledElement, image) - { - var lastRenderedImageId = enabledElement.renderingTools.lastRenderedImageId; - var lastRenderedViewport = enabledElement.renderingTools.lastRenderedViewport; - - if(image.imageId !== lastRenderedImageId || - lastRenderedViewport.windowCenter !== enabledElement.viewport.voi.windowCenter || - lastRenderedViewport.windowWidth !== enabledElement.viewport.voi.windowWidth || - lastRenderedViewport.invert !== enabledElement.viewport.invert || - lastRenderedViewport.rotation !== enabledElement.viewport.rotation || - lastRenderedViewport.hflip !== enabledElement.viewport.hflip || - lastRenderedViewport.vflip !== enabledElement.viewport.vflip - ) - { - return true; - } - - return false; - } - - function getRenderCanvas(enabledElement, image, invalidated) - { - if (!enabledElement.renderingTools.colorRenderCanvas) { - enabledElement.renderingTools.colorRenderCanvas = document.createElement('canvas'); - } - - var colorRenderCanvas = enabledElement.renderingTools.colorRenderCanvas; - - // The ww/wc is identity and not inverted - get a canvas with the image rendered into it for - // fast drawing - if(enabledElement.viewport.voi.windowWidth === 255 && - enabledElement.viewport.voi.windowCenter === 128 && - enabledElement.viewport.invert === false && - image.getCanvas && - image.getCanvas() - ) - { - return image.getCanvas(); - } - - // apply the lut to the stored pixel data onto the render canvas - if(doesImageNeedToBeRendered(enabledElement, image) === false && invalidated !== true) { - return colorRenderCanvas; - } - - // If our render canvas does not match the size of this image reset it - // NOTE: This might be inefficient if we are updating multiple images of different - // sizes frequently. - if(colorRenderCanvas.width !== image.width || colorRenderCanvas.height !== image.height) { - initializeColorRenderCanvas(enabledElement, image); - } - - // get the lut to use - var colorLut = getLut(image, enabledElement.viewport); - - var colorRenderCanvasData = enabledElement.renderingTools.colorRenderCanvasData; - var colorRenderCanvasContext = enabledElement.renderingTools.colorRenderCanvasContext; - // the color image voi/invert has been modified - apply the lut to the underlying - // pixel data and put it into the renderCanvas - cornerstone.storedColorPixelDataToCanvasImageData(image, colorLut, colorRenderCanvasData.data); - colorRenderCanvasContext.putImageData(colorRenderCanvasData, 0, 0); - return colorRenderCanvas; - } - - /** - * API function to render a color image to an enabled element - * @param enabledElement - * @param invalidated - true if pixel data has been invaldiated and cached rendering should not be used - */ - function renderColorImage(enabledElement, invalidated) { - - if(enabledElement === undefined) { - throw "drawImage: enabledElement parameter must not be undefined"; - } - var image = enabledElement.image; - if(image === undefined) { - throw "drawImage: image must be loaded before it can be drawn"; - } - - // get the canvas context and reset the transform - var context = enabledElement.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - // clear the canvas - context.fillStyle = 'black'; - context.fillRect(0,0, enabledElement.canvas.width, enabledElement.canvas.height); - - // turn off image smooth/interpolation if pixelReplication is set in the viewport - if(enabledElement.viewport.pixelReplication === true) { - context.imageSmoothingEnabled = false; - context.mozImageSmoothingEnabled = false; // firefox doesn't support imageSmoothingEnabled yet - } - else { - context.imageSmoothingEnabled = true; - context.mozImageSmoothingEnabled = true; - } - - // save the canvas context state and apply the viewport properties - context.save(); - cornerstone.setToPixelCoordinateSystem(enabledElement, context); - - if (!enabledElement.renderingTools) { - enabledElement.renderingTools = {}; - } - - var renderCanvas; - if (enabledElement.options && enabledElement.options.renderer && - enabledElement.options.renderer.toLowerCase() === 'webgl') { - // If this enabled element has the option set for WebGL, we should - // user it as our renderer. - renderCanvas = cornerstone.webGL.renderer.render(enabledElement); - } else { - // If no options are set we will retrieve the renderCanvas through the - // normal Canvas rendering path - renderCanvas = getRenderCanvas(enabledElement, image, invalidated); - } - - context.drawImage(renderCanvas, 0,0, image.width, image.height, 0, 0, image.width, image.height); - - context.restore(); - - enabledElement.renderingTools.lastRenderedImageId = image.imageId; - var lastRenderedViewport = {}; - lastRenderedViewport.windowCenter = enabledElement.viewport.voi.windowCenter; - lastRenderedViewport.windowWidth = enabledElement.viewport.voi.windowWidth; - lastRenderedViewport.invert = enabledElement.viewport.invert; - lastRenderedViewport.rotation = enabledElement.viewport.rotation; - lastRenderedViewport.hflip = enabledElement.viewport.hflip; - lastRenderedViewport.vflip = enabledElement.viewport.vflip; - enabledElement.renderingTools.lastRenderedViewport = lastRenderedViewport; - } - - // Module exports - cornerstone.rendering.colorImage = renderColorImage; - cornerstone.renderColorImage = renderColorImage; -}(cornerstone)); - -/** - * This module is responsible for drawing a grayscale image - */ - -(function (cornerstone) { - - "use strict"; - - function initializeGrayscaleRenderCanvas(enabledElement, image) - { - var grayscaleRenderCanvas = enabledElement.renderingTools.grayscaleRenderCanvas; - // Resize the canvas - grayscaleRenderCanvas.width = image.width; - grayscaleRenderCanvas.height = image.height; - - // NOTE - we need to fill the render canvas with white pixels since we control the luminance - // using the alpha channel to improve rendering performance. - var grayscaleRenderCanvasContext = grayscaleRenderCanvas.getContext('2d'); - grayscaleRenderCanvasContext.fillStyle = 'white'; - grayscaleRenderCanvasContext.fillRect(0,0, grayscaleRenderCanvas.width, grayscaleRenderCanvas.height); - var grayscaleRenderCanvasData = grayscaleRenderCanvasContext.getImageData(0,0,image.width, image.height); - - enabledElement.renderingTools.grayscaleRenderCanvasContext = grayscaleRenderCanvasContext; - enabledElement.renderingTools.grayscaleRenderCanvasData = grayscaleRenderCanvasData; - } - - function lutMatches(a, b) { - // if undefined, they are equal - if(!a && !b) { - return true; - } - // if one is undefined, not equal - if(!a || !b) { - return false; - } - // check the unique ids - return (a.id === b.id) - } - - function getLut(image, viewport, invalidated) - { - // if we have a cached lut and it has the right values, return it immediately - if(image.lut !== undefined && - image.lut.windowCenter === viewport.voi.windowCenter && - image.lut.windowWidth === viewport.voi.windowWidth && - lutMatches(image.lut.modalityLUT, viewport.modalityLUT) && - lutMatches(image.lut.voiLUT, viewport.voiLUT) && - image.lut.invert === viewport.invert && - invalidated !== true) { - return image.lut; - } - - // lut is invalid or not present, regenerate it and cache it - cornerstone.generateLut(image, viewport.voi.windowWidth, viewport.voi.windowCenter, viewport.invert, viewport.modalityLUT, viewport.voiLUT); - image.lut.windowWidth = viewport.voi.windowWidth; - image.lut.windowCenter = viewport.voi.windowCenter; - image.lut.invert = viewport.invert; - image.lut.voiLUT = viewport.voiLUT; - image.lut.modalityLUT = viewport.modalityLUT; - return image.lut; - } - - function doesImageNeedToBeRendered(enabledElement, image) - { - var lastRenderedImageId = enabledElement.renderingTools.lastRenderedImageId; - var lastRenderedViewport = enabledElement.renderingTools.lastRenderedViewport; - - if(image.imageId !== lastRenderedImageId || - lastRenderedViewport.windowCenter !== enabledElement.viewport.voi.windowCenter || - lastRenderedViewport.windowWidth !== enabledElement.viewport.voi.windowWidth || - lastRenderedViewport.invert !== enabledElement.viewport.invert || - lastRenderedViewport.rotation !== enabledElement.viewport.rotation || - lastRenderedViewport.hflip !== enabledElement.viewport.hflip || - lastRenderedViewport.vflip !== enabledElement.viewport.vflip || - lastRenderedViewport.modalityLUT !== enabledElement.viewport.modalityLUT || - lastRenderedViewport.voiLUT !== enabledElement.viewport.voiLUT - ) - { - return true; - } - - return false; - } - - function getRenderCanvas(enabledElement, image, invalidated) - { - if (!enabledElement.renderingTools.grayscaleRenderCanvas) { - enabledElement.renderingTools.grayscaleRenderCanvas = document.createElement('canvas'); - } - - var grayscaleRenderCanvas = enabledElement.renderingTools.grayscaleRenderCanvas; - - // apply the lut to the stored pixel data onto the render canvas - - if(doesImageNeedToBeRendered(enabledElement, image) === false && invalidated !== true) { - return grayscaleRenderCanvas; - } - - // If our render canvas does not match the size of this image reset it - // NOTE: This might be inefficient if we are updating multiple images of different - // sizes frequently. - if(grayscaleRenderCanvas.width !== image.width || grayscaleRenderCanvas.height !== image.height) { - initializeGrayscaleRenderCanvas(enabledElement, image); - } - - // get the lut to use - var lut = getLut(image, enabledElement.viewport, invalidated); - - var grayscaleRenderCanvasData = enabledElement.renderingTools.grayscaleRenderCanvasData; - var grayscaleRenderCanvasContext = enabledElement.renderingTools.grayscaleRenderCanvasContext; - // gray scale image - apply the lut and put the resulting image onto the render canvas - cornerstone.storedPixelDataToCanvasImageData(image, lut, grayscaleRenderCanvasData.data); - grayscaleRenderCanvasContext.putImageData(grayscaleRenderCanvasData, 0, 0); - return grayscaleRenderCanvas; - } - - /** - * API function to draw a grayscale image to a given enabledElement - * @param enabledElement - * @param invalidated - true if pixel data has been invaldiated and cached rendering should not be used - */ - function renderGrayscaleImage(enabledElement, invalidated) { - if (enabledElement === undefined) { - throw "drawImage: enabledElement parameter must not be undefined"; - } - - var image = enabledElement.image; - if (image === undefined) { - throw "drawImage: image must be loaded before it can be drawn"; - } - - // get the canvas context and reset the transform - var context = enabledElement.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - // clear the canvas - context.fillStyle = 'black'; - context.fillRect(0,0, enabledElement.canvas.width, enabledElement.canvas.height); - - // turn off image smooth/interpolation if pixelReplication is set in the viewport - if(enabledElement.viewport.pixelReplication === true) { - context.imageSmoothingEnabled = false; - context.mozImageSmoothingEnabled = false; // firefox doesn't support imageSmoothingEnabled yet - } - else { - context.imageSmoothingEnabled = true; - context.mozImageSmoothingEnabled = true; - } - - // Save the canvas context state and apply the viewport properties - cornerstone.setToPixelCoordinateSystem(enabledElement, context); - - if (!enabledElement.renderingTools) { - enabledElement.renderingTools = {}; - } - - var renderCanvas; - if (enabledElement.options && enabledElement.options.renderer && - enabledElement.options.renderer.toLowerCase() === 'webgl') { - // If this enabled element has the option set for WebGL, we should - // user it as our renderer. - renderCanvas = cornerstone.webGL.renderer.render(enabledElement); - } else { - // If no options are set we will retrieve the renderCanvas through the - // normal Canvas rendering path - renderCanvas = getRenderCanvas(enabledElement, image, invalidated); - } - - // Draw the render canvas half the image size (because we set origin to the middle of the canvas above) - context.drawImage(renderCanvas, 0,0, image.width, image.height, 0, 0, image.width, image.height); - - enabledElement.renderingTools.lastRenderedImageId = image.imageId; - var lastRenderedViewport = {}; - lastRenderedViewport.windowCenter = enabledElement.viewport.voi.windowCenter; - lastRenderedViewport.windowWidth = enabledElement.viewport.voi.windowWidth; - lastRenderedViewport.invert = enabledElement.viewport.invert; - lastRenderedViewport.rotation = enabledElement.viewport.rotation; - lastRenderedViewport.hflip = enabledElement.viewport.hflip; - lastRenderedViewport.vflip = enabledElement.viewport.vflip; - lastRenderedViewport.modalityLUT = enabledElement.viewport.modalityLUT; - lastRenderedViewport.voiLUT = enabledElement.viewport.voiLUT; - enabledElement.renderingTools.lastRenderedViewport = lastRenderedViewport; - } - - // Module exports - cornerstone.rendering.grayscaleImage = renderGrayscaleImage; - cornerstone.renderGrayscaleImage = renderGrayscaleImage; - -}(cornerstone)); - -/** - * This module is responsible for drawing an image to an enabled elements canvas element - */ - -(function (cornerstone) { - - "use strict"; - - /** - * API function to draw a standard web image (PNG, JPG) to an enabledImage - * - * @param enabledElement - * @param invalidated - true if pixel data has been invaldiated and cached rendering should not be used - */ - function renderWebImage(enabledElement, invalidated) { - - if(enabledElement === undefined) { - throw "drawImage: enabledElement parameter must not be undefined"; - } - var image = enabledElement.image; - if(image === undefined) { - throw "drawImage: image must be loaded before it can be drawn"; - } - - // get the canvas context and reset the transform - var context = enabledElement.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - // clear the canvas - context.fillStyle = 'black'; - context.fillRect(0,0, enabledElement.canvas.width, enabledElement.canvas.height); - - // turn off image smooth/interpolation if pixelReplication is set in the viewport - if(enabledElement.viewport.pixelReplication === true) { - context.imageSmoothingEnabled = false; - context.mozImageSmoothingEnabled = false; // firefox doesn't support imageSmoothingEnabled yet - } - else { - context.imageSmoothingEnabled = true; - context.mozImageSmoothingEnabled = true; - } - - // save the canvas context state and apply the viewport properties - cornerstone.setToPixelCoordinateSystem(enabledElement, context); - - // if the viewport ww/wc and invert all match the initial state of the image, we can draw the image - // directly. If any of those are changed, we call renderColorImage() to apply the lut - if(enabledElement.viewport.voi.windowWidth === enabledElement.image.windowWidth && - enabledElement.viewport.voi.windowCenter === enabledElement.image.windowCenter && - enabledElement.viewport.invert === false) - { - context.drawImage(image.getImage(), 0, 0, image.width, image.height, 0, 0, image.width, image.height); - } else { - cornerstone.renderColorImage(enabledElement, invalidated); - } - - } - - // Module exports - cornerstone.rendering.webImage = renderWebImage; - cornerstone.renderWebImage = renderWebImage; - -}(cornerstone)); -/** - */ -(function (cornerstone) { - - "use strict"; - - /** - * Resets the viewport to the default settings - * - * @param element - */ - function reset(element) - { - var enabledElement = cornerstone.getEnabledElement(element); - var defaultViewport = cornerstone.internal.getDefaultViewport(enabledElement.canvas, enabledElement.image); - enabledElement.viewport = defaultViewport; - cornerstone.updateImage(element); - } - - cornerstone.reset = reset; -}(cornerstone)); - -/** - * This module is responsible for enabling an element to display images with cornerstone - */ -(function (cornerstone) { - - "use strict"; - - function setCanvasSize(element, canvas) - { - // the device pixel ratio is 1.0 for normal displays and > 1.0 - // for high DPI displays like Retina - /* - - This functionality is disabled due to buggy behavior on systems with mixed DPI's. If the canvas - is created on a display with high DPI (e.g. 2.0) and then the browser window is dragged to - a different display with a different DPI (e.g. 1.0), the canvas is not recreated so the pageToPixel - produces incorrect results. I couldn't find any way to determine when the DPI changed other than - by polling which is not very clean. If anyone has any ideas here, please let me know, but for now - we will disable this functionality. We may want - to add a mechanism to optionally enable this functionality if we can determine it is safe to do - so (e.g. iPad or iPhone or perhaps enumerate the displays on the system. I am choosing - to be cautious here since I would rather not have bug reports or safety issues related to this - scenario. - - var devicePixelRatio = window.devicePixelRatio; - if(devicePixelRatio === undefined) { - devicePixelRatio = 1.0; - } - */ - - canvas.width = element.clientWidth; - canvas.height = element.clientHeight; - canvas.style.width = element.clientWidth + "px"; - canvas.style.height = element.clientHeight + "px"; - } - - /** - * resizes an enabled element and optionally fits the image to window - * @param element - * @param fitToWindow true to refit, false to leave viewport parameters as they are - */ - function resize(element, fitToWindow) { - - var enabledElement = cornerstone.getEnabledElement(element); - - setCanvasSize(element, enabledElement.canvas); - - var eventData = { - element: element - }; - - $(element).trigger("CornerstoneElementResized", eventData); - - if(enabledElement.image === undefined ) { - return; - } - - if(fitToWindow === true) { - cornerstone.fitToWindow(element); - } - else { - cornerstone.updateImage(element); - } - } - - // module/private exports - cornerstone.resize = resize; - -}(cornerstone)); - -/** - * This module contains a function that will set the canvas context to the pixel coordinates system - * making it easy to draw geometry on the image - */ - -(function (cornerstone) { - - "use strict"; - - /** - * Sets the canvas context transformation matrix to the pixel coordinate system. This allows - * geometry to be driven using the canvas context using coordinates in the pixel coordinate system - * @param ee - * @param context - * @param scale optional scaler to apply - */ - function setToPixelCoordinateSystem(enabledElement, context, scale) - { - if(enabledElement === undefined) { - throw "setToPixelCoordinateSystem: parameter enabledElement must not be undefined"; - } - if(context === undefined) { - throw "setToPixelCoordinateSystem: parameter context must not be undefined"; - } - - var transform = cornerstone.internal.calculateTransform(enabledElement, scale); - context.setTransform(transform.m[0],transform.m[1],transform.m[2],transform.m[3],transform.m[4],transform.m[5]); - } - - // Module exports - cornerstone.setToPixelCoordinateSystem = setToPixelCoordinateSystem; -}(cornerstone)); -/** - * This module contains functions to deal with getting and setting the viewport for an enabled element - */ -(function (cornerstone) { - - "use strict"; - - /** - * Sets the viewport for an element and corrects invalid values - * - * @param element - DOM element of the enabled element - * @param viewport - Object containing the viewport properties - * @returns {*} - */ - function setViewport(element, viewport) { - - var enabledElement = cornerstone.getEnabledElement(element); - - enabledElement.viewport.scale = viewport.scale; - enabledElement.viewport.translation.x = viewport.translation.x; - enabledElement.viewport.translation.y = viewport.translation.y; - enabledElement.viewport.voi.windowWidth = viewport.voi.windowWidth; - enabledElement.viewport.voi.windowCenter = viewport.voi.windowCenter; - enabledElement.viewport.invert = viewport.invert; - enabledElement.viewport.pixelReplication = viewport.pixelReplication; - enabledElement.viewport.rotation = viewport.rotation; - enabledElement.viewport.hflip = viewport.hflip; - enabledElement.viewport.vflip = viewport.vflip; - enabledElement.viewport.modalityLUT = viewport.modalityLUT; - enabledElement.viewport.voiLUT = viewport.voiLUT; - - // prevent window width from being too small (note that values close to zero are valid and can occur with - // PET images in particular) - if(enabledElement.viewport.voi.windowWidth < 0.000001) { - enabledElement.viewport.voi.windowWidth = 0.000001; - } - // prevent scale from getting too small - if(enabledElement.viewport.scale < 0.0001) { - enabledElement.viewport.scale = 0.25; - } - - if(enabledElement.viewport.rotation===360 || enabledElement.viewport.rotation===-360) { - enabledElement.viewport.rotation = 0; - } - - // Force the image to be updated since the viewport has been modified - cornerstone.updateImage(element); - } - - - // module/private exports - cornerstone.setViewport = setViewport; - -}(cornerstone)); - -/** - * This module contains a function to immediately redraw an image - */ -(function (cornerstone) { - - "use strict"; - - /** - * Forces the image to be updated/redrawn for the specified enabled element - * @param element - */ - function updateImage(element, invalidated) { - var enabledElement = cornerstone.getEnabledElement(element); - - if(enabledElement.image === undefined) { - throw "updateImage: image has not been loaded yet"; - } - - cornerstone.drawImage(enabledElement, invalidated); - } - - // module exports - cornerstone.updateImage = updateImage; - -}(cornerstone)); -(function (cornerstone) { - - "use strict"; - - if (!cornerstone.webGL) { - cornerstone.webGL = {}; - } - - /** - * Creates and compiles a shader. - * - * @param {!WebGLRenderingContext} gl The WebGL Context. - * @param {string} shaderSource The GLSL source code for the shader. - * @param {number} shaderType The type of shader, VERTEX_SHADER or FRAGMENT_SHADER. - * - * @return {!WebGLShader} The shader. - */ - function compileShader(gl, shaderSource, shaderType) { - - // Create the shader object - var shader = gl.createShader(shaderType); - - // Set the shader source code. - gl.shaderSource(shader, shaderSource); - - // Compile the shader - gl.compileShader(shader); - - // Check if it compiled - var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); - if (!success && !gl.isContextLost()) { - // Something went wrong during compilation; get the error - var infoLog = gl.getShaderInfoLog(shader); - console.error("Could not compile shader:\n" + infoLog); - } - - return shader; - } - - /** - * Creates a program from 2 shaders. - * - * @param {!WebGLRenderingContext) gl The WebGL context. - * @param {!WebGLShader} vertexShader A vertex shader. - * @param {!WebGLShader} fragmentShader A fragment shader. - * @return {!WebGLProgram} A program. - */ - function createProgram(gl, vertexShader, fragmentShader) { - - // create a program. - var program = gl.createProgram(); - - // attach the shaders. - gl.attachShader(program, vertexShader); - gl.attachShader(program, fragmentShader); - - // link the program. - gl.linkProgram(program); - - // Check if it linked. - var success = gl.getProgramParameter(program, gl.LINK_STATUS); - if (!success && !gl.isContextLost()) { - // something went wrong with the link - var infoLog = gl.getProgramInfoLog(program); - console.error("WebGL program filed to link:\n" + infoLog); - } - - return program; - } - - /** - * Creates a program from 2 shaders source (Strings) - * @param {!WebGLRenderingContext} gl The WebGL context. - * @param {!WebGLShader} vertexShaderSrc Vertex shader string - * @param {!WebGLShader} fragShaderSrc Fragment shader string - * @return {!WebGLProgram} A program - */ - function createProgramFromString(gl, vertexShaderSrc, fragShaderSrc) { - var vertexShader = compileShader(gl, vertexShaderSrc, gl.VERTEX_SHADER); - var fragShader = compileShader(gl, fragShaderSrc, gl.FRAGMENT_SHADER); - return createProgram(gl, vertexShader, fragShader); - } - - cornerstone.webGL.createProgramFromString = createProgramFromString; - -}(cornerstone)); - -(function (cornerstone) { - - "use strict"; - - if (!cornerstone.webGL) { - cornerstone.webGL = {}; - } - - var renderCanvas = document.createElement('canvas'); - var renderCanvasContext; - var renderCanvasData; - var gl; - var programs; - var shader; - var texCoordBuffer, positionBuffer; - cornerstone.webGL.isWebGLInitialized = false; - - function getRenderCanvas() { - return renderCanvas; - } - - function initShaders() { - for (var id in cornerstone.webGL.shaders) { - //console.log("WEBGL: Loading shader", id); - var shader = cornerstone.webGL.shaders[ id ]; - shader.attributes = {}; - shader.uniforms = {}; - shader.vert = cornerstone.webGL.vertexShader; - - shader.program = cornerstone.webGL.createProgramFromString(gl, shader.vert, shader.frag); - - shader.attributes.texCoordLocation = gl.getAttribLocation(shader.program, "a_texCoord"); - gl.enableVertexAttribArray(shader.attributes.texCoordLocation); - - shader.attributes.positionLocation = gl.getAttribLocation(shader.program, "a_position"); - gl.enableVertexAttribArray(shader.attributes.positionLocation); - - shader.uniforms.resolutionLocation = gl.getUniformLocation(shader.program, "u_resolution"); - } - } - - function initRenderer() { - if (cornerstone.webGL.isWebGLInitialized === true) { - //console.log("WEBGL Renderer already initialized"); - return; - } - - if (initWebGL(renderCanvas)) { - initBuffers(); - initShaders(); - //console.log("WEBGL Renderer initialized!"); - cornerstone.webGL.isWebGLInitialized = true; - } - } - - function updateRectangle(gl, width, height) { - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ - width, height, - 0, height, - width, 0, - 0, 0]), gl.STATIC_DRAW); - } - - function handleLostContext(event) { - event.preventDefault(); - console.warn('WebGL Context Lost!'); - } - - function handleRestoredContext(event) { - event.preventDefault(); - cornerstone.webGL.isWebGLInitialized = false; - cornerstone.webGL.textureCache.purgeCache(); - initRenderer(); - //console.log('WebGL Context Restored.'); - } - - function initWebGL(canvas) { - - gl = null; - try { - // Try to grab the standard context. If it fails, fallback to experimental. - var options = { - preserveDrawingBuffer: true, // preserve buffer so we can copy to display canvas element - }; - - // ---------------- Testing purposes ------------- - if (cornerstone.webGL.debug === true && WebGLDebugUtils) { - renderCanvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(renderCanvas); - } - // ---------------- Testing purposes ------------- - - gl = canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options); - - // Set up event listeners for context lost / context restored - canvas.removeEventListener("webglcontextlost", handleLostContext, false); - canvas.addEventListener("webglcontextlost", handleLostContext, false); - - canvas.removeEventListener("webglcontextrestored", handleRestoredContext, false); - canvas.addEventListener("webglcontextrestored", handleRestoredContext, false); - - } catch(error) { - throw "Error creating WebGL context"; - } - - // If we don't have a GL context, give up now - if (!gl) { - console.error("Unable to initialize WebGL. Your browser may not support it."); - gl = null; - } - return gl; - } - - function getImageDataType(image) { - if (image.color) { - return 'rgb'; - } - - var datatype = 'int'; - if (image.minPixelValue >= 0) { - datatype = 'u' + datatype; - } - - if (image.maxPixelValue > 255) { - datatype += '16'; - } else { - datatype += '8'; - } - return datatype; - } - - function getShaderProgram(image) { - - var datatype = getImageDataType(image); - // We need a mechanism for - // choosing the shader based on the image datatype - // console.log("Datatype: " + datatype); - if (cornerstone.webGL.shaders.hasOwnProperty(datatype)) { - return cornerstone.webGL.shaders[datatype]; - } - - var shader = cornerstone.webGL.shaders.rgb; - return shader; - } - - function getImageTexture( image ) { - var imageTexture = cornerstone.webGL.textureCache.getImageTexture(image.imageId); - if (!imageTexture) { - //console.log("Generating texture for imageid: ", image.imageId); - imageTexture = generateTexture(image); - cornerstone.webGL.textureCache.putImageTexture(image, imageTexture); - } - return imageTexture.texture; - - } - - function generateTexture( image ) { - var TEXTURE_FORMAT = { - uint8: gl.LUMINANCE, - int8: gl.LUMINANCE_ALPHA, - uint16: gl.LUMINANCE_ALPHA, - int16: gl.RGB, - rgb: gl.RGB - }; - - var TEXTURE_BYTES = { - int8: 1, // Luminance - uint16: 2, // Luminance + Alpha - int16: 3, // RGB - rgb: 3 // RGB - }; - - var imageDataType = getImageDataType(image); - var format = TEXTURE_FORMAT[imageDataType]; - - // GL texture configuration - var texture = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, texture); - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); - - var imageData = cornerstone.webGL.dataUtilities[imageDataType].storedPixelDataToImageData(image, image.width, image.height); - - gl.texImage2D(gl.TEXTURE_2D, 0, format, image.width, image.height, 0, format, gl.UNSIGNED_BYTE, imageData); - - // Calculate the size in bytes of this image in memory - var sizeInBytes = image.width * image.height * TEXTURE_BYTES[imageDataType]; - var imageTexture = { - texture: texture, - sizeInBytes: sizeInBytes - }; - return imageTexture; - - } - - function initBuffers() { - positionBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ - 1, 1, - 0, 1, - 1, 0, - 0, 0 - ]), gl.STATIC_DRAW); - - - texCoordBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ - 1.0, 1.0, - 0.0, 1.0, - 1.0, 0.0, - 0.0, 0.0, - ]), gl.STATIC_DRAW); - } - - function renderQuad(shader, parameters, texture, width, height) { - gl.clearColor(1.0,0.0,0.0,1.0); - gl.viewport( 0, 0, width, height ); - - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.useProgram(shader.program); - - gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); - gl.vertexAttribPointer(shader.attributes.texCoordLocation, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); - gl.vertexAttribPointer(shader.attributes.positionLocation, 2, gl.FLOAT, false, 0, 0); - - for (var key in parameters) { - var uniformLocation = gl.getUniformLocation(shader.program, key); - if ( !uniformLocation ) { - throw "Could not access location for uniform: " + key; - } - - var uniform = parameters[key]; - - var type = uniform.type; - var value = uniform.value; - - if( type == "i" ) { - gl.uniform1i( uniformLocation, value ); - } else if( type == "f" ) { - gl.uniform1f( uniformLocation, value ); - } else if( type == "2f" ) { - gl.uniform2f( uniformLocation, value[0], value[1] ); - } - } - - updateRectangle(gl, width, height); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); - - } - - function render(enabledElement) { - // Resize the canvas - var image = enabledElement.image; - renderCanvas.width = image.width; - renderCanvas.height = image.height; - - var viewport = enabledElement.viewport; - - // Render the current image - var shader = getShaderProgram(image); - var texture = getImageTexture(image); - var parameters = { - "u_resolution": { type: "2f", value: [image.width, image.height] }, - "wc": { type: "f", value: viewport.voi.windowCenter }, - "ww": { type: "f", value: viewport.voi.windowWidth }, - "slope": { type: "f", value: image.slope }, - "intercept": { type: "f", value: image.intercept }, - //"minPixelValue": { type: "f", value: image.minPixelValue }, - "invert": { type: "i", value: viewport.invert ? 1 : 0 }, - }; - renderQuad(shader, parameters, texture, image.width, image.height ); - - return renderCanvas; - } - - function isWebGLAvailable() { - // Adapted from - // http://stackoverflow.com/questions/9899807/three-js-detect-webgl-support-and-fallback-to-regular-canvas - - var options = { - failIfMajorPerformanceCaveat: true - }; - - try { - var canvas = document.createElement("canvas"); - return !! - window.WebGLRenderingContext && - (canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options)); - } catch(e) { - return false; - } - } - - cornerstone.webGL.renderer = { - render: render, - initRenderer: initRenderer, - getRenderCanvas: getRenderCanvas, - isWebGLAvailable: isWebGLAvailable - }; - -}(cornerstone)); - - -(function (cornerstone) { - - "use strict"; - - if (!cornerstone.webGL) { - cornerstone.webGL = {}; - } - - if (!cornerstone.webGL.shaders) { - cornerstone.webGL.shaders = {}; - } - - if (!cornerstone.webGL.dataUtilities) { - cornerstone.webGL.dataUtilities = {}; - } - - // Pack int16 into three uint8 channels (r, g, b) - var shader = {}; - - function storedPixelDataToImageData(image) { - - // Transfer image data to alpha and luminance channels of WebGL texture - // Credit to @jpambrun and @fernandojsg - - // Pack int16 into three uint8 channels (r, g, b) - var pixelData = image.getPixelData(); - var numberOfChannels = 3; - var data = new Uint8Array(image.width * image.height * numberOfChannels); - var offset = 0; - - for (var i = 0; i < pixelData.length; i++) { - var val = Math.abs(pixelData[i]); - data[offset++] = parseInt(val & 0xFF, 10); - data[offset++] = parseInt(val >> 8, 10); - data[offset++] = pixelData[i] < 0 ? 0: 1; // 0 For negative, 1 for positive - } - return data; - } - - cornerstone.webGL.dataUtilities.int16 = { - storedPixelDataToImageData: storedPixelDataToImageData - }; - - shader.frag = 'precision mediump float;' + - 'uniform sampler2D u_image;' + - 'uniform float ww;' + - 'uniform float wc;' + - 'uniform float slope;' + - 'uniform float intercept;' + - 'uniform int invert;' + - 'varying vec2 v_texCoord;' + - - 'void main() {' + - // Get texture - 'vec4 color = texture2D(u_image, v_texCoord);' + - - // Calculate luminance from packed texture - 'float intensity = color.r*256.0 + color.g*65536.0;'+ - - 'if (color.b == 0.0)' + - 'intensity = -intensity;' + - - // Rescale based on slope and window settings - 'intensity = intensity * slope + intercept;'+ - 'float center0 = wc - 0.5;'+ - 'float width0 = max(ww, 1.0);' + - 'intensity = (intensity - center0) / width0 + 0.5;'+ - - // Clamp intensity - 'intensity = clamp(intensity, 0.0, 1.0);' + - - // RGBA output - 'gl_FragColor = vec4(intensity, intensity, intensity, 1.0);' + - - // Apply any inversion necessary - 'if (invert == 1)' + - 'gl_FragColor.rgb = 1.0 - gl_FragColor.rgb;' + - '}'; - - cornerstone.webGL.shaders.int16 = shader; - -}(cornerstone)); -(function (cornerstone) { - - "use strict"; - - if (!cornerstone.webGL) { - cornerstone.webGL = {}; - } - - if (!cornerstone.webGL.shaders) { - cornerstone.webGL.shaders = {}; - } - - if (!cornerstone.webGL.dataUtilities) { - cornerstone.webGL.dataUtilities = {}; - } - - var shader = {}; - - function storedPixelDataToImageData(image) { - // Transfer image data to alpha channel of WebGL texture - // Store data in Uint8Array - var pixelData = image.getPixelData(); - var numberOfChannels = 2; - var data = new Uint8Array(image.width * image.height * numberOfChannels); - var offset = 0; - - for (var i = 0; i < pixelData.length; i++) { - data[offset++] = parseInt(pixelData[i], 10); - data[offset++] = pixelData[i] < 0 ? 0: 1; // 0 For negative, 1 for positive - } - return data; - } - - cornerstone.webGL.dataUtilities.int8 = { - storedPixelDataToImageData: storedPixelDataToImageData - }; - - shader.frag = 'precision mediump float;' + - 'uniform sampler2D u_image;' + - 'uniform float ww;' + - 'uniform float wc;' + - 'uniform float slope;' + - 'uniform float intercept;' + - 'uniform float minPixelValue;' + - 'uniform int invert;' + - 'varying vec2 v_texCoord;' + - - 'void main() {' + - // Get texture - 'vec4 color = texture2D(u_image, v_texCoord);' + - - // Calculate luminance from packed texture - 'float intensity = color.r*256.;'+ - - 'if (color.a == 0.0)' + - 'intensity = -intensity;' + - - // Rescale based on slope and window settings - 'intensity = intensity * slope + intercept;'+ - 'float center0 = wc - 0.5;'+ - 'float width0 = max(ww, 1.0);' + - 'intensity = (intensity - center0) / width0 + 0.5;'+ - - // Clamp intensity - 'intensity = clamp(intensity, 0.0, 1.0);' + - - // RGBA output - 'gl_FragColor = vec4(intensity, intensity, intensity, 1.0);' + - - // Apply any inversion necessary - 'if (invert == 1)' + - 'gl_FragColor.rgb = 1.0 - gl_FragColor.rgb;' + - '}'; - - cornerstone.webGL.shaders.int8 = shader; - - -}(cornerstone)); -(function (cornerstone) { - - "use strict"; - - if (!cornerstone.webGL) { - cornerstone.webGL = {}; - } - - if (!cornerstone.webGL.shaders) { - cornerstone.webGL.shaders = {}; - } - - if (!cornerstone.webGL.dataUtilities) { - cornerstone.webGL.dataUtilities = {}; - } - - // Pack RGB images into a 3-channel RGB texture - var shader = {}; - - function storedPixelDataToImageData(image) { - var minPixelValue = image.minPixelValue; - var canvasImageDataIndex = 0; - var storedPixelDataIndex = 0; - // Only 3 channels, since we use WebGL's RGB texture format - var numStoredPixels = image.width * image.height * 4; - var numOutputPixels = image.width * image.height * 3; - var storedPixelData = image.getPixelData(); - var data = new Uint8Array(numOutputPixels); - - // NOTE: As of Nov 2014, most javascript engines have lower performance when indexing negative indexes. - // We have a special code path for this case that improves performance. Thanks to @jpambrun for this enhancement - if (minPixelValue < 0){ - while (storedPixelDataIndex < numStoredPixels) { - data[canvasImageDataIndex++] = storedPixelData[storedPixelDataIndex++] + (-minPixelValue); // red - data[canvasImageDataIndex++] = storedPixelData[storedPixelDataIndex++] + (-minPixelValue); // green - data[canvasImageDataIndex++] = storedPixelData[storedPixelDataIndex++] + (-minPixelValue); // blue - storedPixelDataIndex += 1; // The stored pixel data has 4 channels - } - } else { - while (storedPixelDataIndex < numStoredPixels) { - data[canvasImageDataIndex++] = storedPixelData[storedPixelDataIndex++]; // red - data[canvasImageDataIndex++] = storedPixelData[storedPixelDataIndex++]; // green - data[canvasImageDataIndex++] = storedPixelData[storedPixelDataIndex++]; // blue - storedPixelDataIndex += 1; // The stored pixel data has 4 channels - } - } - return data; - } - - cornerstone.webGL.dataUtilities.rgb = { - storedPixelDataToImageData: storedPixelDataToImageData - }; - - shader.frag = 'precision mediump float;' + - 'uniform sampler2D u_image;' + - 'uniform float ww;' + - 'uniform float wc;' + - 'uniform float slope;' + - 'uniform float intercept;' + - 'uniform float minPixelValue;' + - 'uniform int invert;' + - 'varying vec2 v_texCoord;' + - - 'void main() {' + - - // Get texture - 'vec3 color = texture2D(u_image, v_texCoord).xyz;' + - - // Rescale based on slope and intercept - 'color = color * 256.0 * slope + intercept;' + - - // Apply window settings - 'float center0 = wc - 0.5 - minPixelValue;'+ - 'float width0 = max(ww, 1.0);' + - 'color = (color - center0) / width0 + 0.5;'+ - - // RGBA output - 'gl_FragColor = vec4(color, 1);' + - - // Apply any inversion necessary - 'if (invert == 1)' + - 'gl_FragColor.rgb = 1. - gl_FragColor.rgb;' + - '}'; - - cornerstone.webGL.shaders.rgb = shader; - -}(cornerstone)); -(function (cornerstone) { - - "use strict"; - - if (!cornerstone.webGL) { - cornerstone.webGL = {}; - } - - if (!cornerstone.webGL.shaders) { - cornerstone.webGL.shaders = {}; - } - - if (!cornerstone.webGL.dataUtilities) { - cornerstone.webGL.dataUtilities = {}; - } - - // For uint16 pack uint16 into two uint8 channels (r and a) - var shader = {}; - - function storedPixelDataToImageData(image) { - - // Transfer image data to alpha and luminance channels of WebGL texture - // Credit to @jpambrun and @fernandojsg - - // Pack uint16 into two uint8 channels (r and a) - var pixelData = image.getPixelData(); - var numberOfChannels = 2; - var data = new Uint8Array(image.width * image.height * numberOfChannels); - var offset = 0; - - for (var i = 0; i < pixelData.length; i++) { - var val = pixelData[i]; - data[offset++] = parseInt(val & 0xFF, 10); - data[offset++] = parseInt(val >> 8, 10); - } - return data; - } - - cornerstone.webGL.dataUtilities.uint16 = { - storedPixelDataToImageData: storedPixelDataToImageData - }; - - shader.frag = 'precision mediump float;' + - 'uniform sampler2D u_image;' + - 'uniform float ww;' + - 'uniform float wc;' + - 'uniform float slope;' + - 'uniform float intercept;' + - 'uniform int invert;' + - 'varying vec2 v_texCoord;' + - - 'void main() {' + - // Get texture - 'vec4 color = texture2D(u_image, v_texCoord);' + - - // Calculate luminance from packed texture - 'float intensity = color.r*256.0 + color.a*65536.0;'+ - - // Rescale based on slope and window settings - 'intensity = intensity * slope + intercept;'+ - 'float center0 = wc - 0.5;'+ - 'float width0 = max(ww, 1.0);' + - 'intensity = (intensity - center0) / width0 + 0.5;'+ - - // Clamp intensity - 'intensity = clamp(intensity, 0.0, 1.0);' + - - // RGBA output - 'gl_FragColor = vec4(intensity, intensity, intensity, 1.0);' + - - // Apply any inversion necessary - 'if (invert == 1)' + - 'gl_FragColor.rgb = 1.0 - gl_FragColor.rgb;' + - '}'; - - cornerstone.webGL.shaders.uint16 = shader; - -}(cornerstone)); -(function (cornerstone) { - - "use strict"; - - if (!cornerstone.webGL) { - cornerstone.webGL = {}; - } - - if (!cornerstone.webGL.shaders) { - cornerstone.webGL.shaders = {}; - } - - if (!cornerstone.webGL.dataUtilities) { - cornerstone.webGL.dataUtilities = {}; - } - - var shader = {}; - - function storedPixelDataToImageData(image) { - // Transfer image data to alpha channel of WebGL texture - // Store data in Uuint8Array - var pixelData = image.getPixelData(); - var data = new Uint8Array(pixelData.length); - for (var i = 0; i < pixelData.length; i++) { - data[i] = parseInt(pixelData[i], 10); - } - return data; - } - - cornerstone.webGL.dataUtilities.uint8 = { - storedPixelDataToImageData: storedPixelDataToImageData - }; - - shader.frag = 'precision mediump float;' + - 'uniform sampler2D u_image;' + - 'uniform float ww;' + - 'uniform float wc;' + - 'uniform float slope;' + - 'uniform float intercept;' + - //'uniform float minPixelValue;' + - 'uniform int invert;' + - 'varying vec2 v_texCoord;' + - - 'void main() {' + - // Get texture - 'vec4 color = texture2D(u_image, v_texCoord);' + - - // Calculate luminance from packed texture - 'float intensity = color.r*256.0;'+ - - // Rescale based on slope and window settings - 'intensity = intensity * slope + intercept;'+ - 'float center0 = wc - 0.5;'+ - 'float width0 = max(ww, 1.0);' + - 'intensity = (intensity - center0) / width0 + 0.5;'+ - - // Clamp intensity - 'intensity = clamp(intensity, 0.0, 1.0);' + - - // RGBA output - 'gl_FragColor = vec4(intensity, intensity, intensity, 1.0);' + - - // Apply any inversion necessary - 'if (invert == 1)' + - 'gl_FragColor.rgb = 1.0 - gl_FragColor.rgb;' + - '}'; - - cornerstone.webGL.shaders.uint8 = shader; - - -}(cornerstone)); -/** - * This module deals with caching image textures in VRAM for WebGL - */ - -(function (cornerstone) { - - "use strict"; - - var imageCache = {}; - - var cachedImages = []; - - var maximumSizeInBytes = 1024 * 1024 * 256; // 256 MB - var cacheSizeInBytes = 0; - - function setMaximumSizeBytes(numBytes) { - if (numBytes === undefined) { - throw "setMaximumSizeBytes: parameter numBytes must not be undefined"; - } - if (numBytes.toFixed === undefined) { - throw "setMaximumSizeBytes: parameter numBytes must be a number"; - } - - maximumSizeInBytes = numBytes; - purgeCacheIfNecessary(); - } - - function purgeCacheIfNecessary() { - // if max cache size has not been exceeded, do nothing - if (cacheSizeInBytes <= maximumSizeInBytes) { - return; - } - - // cache size has been exceeded, create list of images sorted by timeStamp - // so we can purge the least recently used image - function compare(a,b) { - if (a.timeStamp > b.timeStamp) { - return -1; - } - if (a.timeStamp < b.timeStamp) { - return 1; - } - return 0; - } - cachedImages.sort(compare); - - // remove images as necessary - while(cacheSizeInBytes > maximumSizeInBytes) { - var lastCachedImage = cachedImages[cachedImages.length - 1]; - cacheSizeInBytes -= lastCachedImage.sizeInBytes; - delete imageCache[lastCachedImage.imageId]; - cachedImages.pop(); - $(cornerstone).trigger('CornerstoneWebGLTextureRemoved', {imageId: lastCachedImage.imageId}); - } - - var cacheInfo = cornerstone.imageCache.getCacheInfo(); - console.log('CornerstoneWebGLTextureCacheFull'); - $(cornerstone).trigger('CornerstoneWebGLTextureCacheFull', cacheInfo); - } - - function putImageTexture(image, imageTexture) { - var imageId = image.imageId; - if (image === undefined) { - throw "putImageTexture: image must not be undefined"; - } - - if (imageId === undefined) { - throw "putImageTexture: imageId must not be undefined"; - } - - if (imageTexture === undefined) { - throw "putImageTexture: imageTexture must not be undefined"; - } - - if (imageCache.hasOwnProperty(imageId) === true) { - throw "putImageTexture: imageId already in cache"; - } - - var cachedImage = { - imageId : imageId, - imageTexture : imageTexture, - timeStamp : new Date(), - sizeInBytes: imageTexture.sizeInBytes - }; - - imageCache[imageId] = cachedImage; - cachedImages.push(cachedImage); - - if (imageTexture.sizeInBytes === undefined) { - throw "putImageTexture: imageTexture does not have sizeInBytes property or"; - } - if (imageTexture.sizeInBytes.toFixed === undefined) { - throw "putImageTexture: imageTexture.sizeInBytes is not a number"; - } - cacheSizeInBytes += cachedImage.sizeInBytes; - purgeCacheIfNecessary(); - } - - function getImageTexture(imageId) { - if (imageId === undefined) { - throw "getImageTexture: imageId must not be undefined"; - } - var cachedImage = imageCache[imageId]; - if (cachedImage === undefined) { - return undefined; - } - - // bump time stamp for cached image - cachedImage.timeStamp = new Date(); - return cachedImage.imageTexture; - } - - function removeImageTexture(imageId) { - if (imageId === undefined) { - throw "removeImageTexture: imageId must not be undefined"; - } - var cachedImage = imageCache[imageId]; - if (cachedImage === undefined) { - throw "removeImageTexture: imageId must not be undefined"; - } - cachedImages.splice( cachedImages.indexOf(cachedImage), 1); - cacheSizeInBytes -= cachedImage.sizeInBytes; - delete imageCache[imageId]; - - return cachedImage.imageTexture; - } - - function getCacheInfo() { - return { - maximumSizeInBytes : maximumSizeInBytes, - cacheSizeInBytes : cacheSizeInBytes, - numberOfImagesCached: cachedImages.length - }; - } - - function purgeCache() { - while (cachedImages.length > 0) { - var removedCachedImage = cachedImages.pop(); - delete imageCache[removedCachedImage.imageId]; - } - cacheSizeInBytes = 0; - } - - // module exports - cornerstone.webGL.textureCache = { - putImageTexture : putImageTexture, - getImageTexture: getImageTexture, - removeImageTexture: removeImageTexture, - setMaximumSizeBytes: setMaximumSizeBytes, - getCacheInfo : getCacheInfo, - purgeCache: purgeCache, - cachedImages: cachedImages - }; - -}(cornerstone)); - -(function (cornerstone) { - - "use strict"; - - if (!cornerstone.webGL) { - cornerstone.webGL = {}; - } - - cornerstone.webGL.vertexShader = 'attribute vec2 a_position;' + - 'attribute vec2 a_texCoord;' + - 'uniform vec2 u_resolution;' + - 'varying vec2 v_texCoord;' + - 'void main() {' + - 'vec2 zeroToOne = a_position / u_resolution;' + - 'vec2 zeroToTwo = zeroToOne * 2.0;' + - 'vec2 clipSpace = zeroToTwo - 1.0;' + - 'gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);' + - 'v_texCoord = a_texCoord;' + - '}'; - -}(cornerstone)); \ No newline at end of file diff --git a/Packages/ohif-cornerstone/client/cornerstoneMath.js b/Packages/ohif-cornerstone/client/cornerstoneMath.js deleted file mode 100644 index 58120cdc6..000000000 --- a/Packages/ohif-cornerstone/client/cornerstoneMath.js +++ /dev/null @@ -1,1995 +0,0 @@ -/*! cornerstoneMath - v0.1.3 - 2016-02-04 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneMath */ -// Begin Source: src/vector3.js -// Based on THREE.JS - -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if(cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - cornerstoneMath.Vector3 = function ( x, y, z ) { - - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - - }; - - cornerstoneMath.Vector3.prototype = { - - constructor: cornerstoneMath.Vector3, - - set: function ( x, y, z ) { - - this.x = x; - this.y = y; - this.z = z; - - return this; - - }, - - setX: function ( x ) { - - this.x = x; - - return this; - - }, - - setY: function ( y ) { - - this.y = y; - - return this; - - }, - - setZ: function ( z ) { - - this.z = z; - - return this; - - }, - - setComponent: function ( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - default: throw new Error( "index is out of range: " + index ); - - } - - }, - - getComponent: function ( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - default: throw new Error( "index is out of range: " + index ); - - } - - }, - - copy: function ( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - - return this; - - }, - - add: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'DEPRECATED: Vector3\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - - return this; - - }, - - addScalar: function ( s ) { - - this.x += s; - this.y += s; - this.z += s; - - return this; - - }, - - addVectors: function ( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - - return this; - - }, - - sub: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'DEPRECATED: Vector3\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - - return this; - - }, - - subVectors: function ( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - - return this; - - }, - - multiply: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'DEPRECATED: Vector3\'s .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); - return this.multiplyVectors( v, w ); - - } - - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - - return this; - - }, - - multiplyScalar: function ( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - - return this; - - }, - - multiplyVectors: function ( a, b ) { - - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; - - return this; - - }, - - - applyAxisAngle: function () { - - var quaternion; - - return function ( axis, angle ) { - - if ( quaternion === undefined ) quaternion = new cornerstoneMath.Quaternion(); - - this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); - - return this; - - }; - - }(), - - applyMatrix3: function ( m ) { - - var x = this.x; - var y = this.y; - var z = this.z; - - var e = m.elements; - - this.x = e[0] * x + e[3] * y + e[6] * z; - this.y = e[1] * x + e[4] * y + e[7] * z; - this.z = e[2] * x + e[5] * y + e[8] * z; - - return this; - - }, - - applyMatrix4: function ( m ) { - - // input: THREE.Matrix4 affine matrix - - var x = this.x, y = this.y, z = this.z; - - var e = m.elements; - - this.x = e[0] * x + e[4] * y + e[8] * z + e[12]; - this.y = e[1] * x + e[5] * y + e[9] * z + e[13]; - this.z = e[2] * x + e[6] * y + e[10] * z + e[14]; - - return this; - - }, - - applyProjection: function ( m ) { - - // input: THREE.Matrix4 projection matrix - - var x = this.x, y = this.y, z = this.z; - - var e = m.elements; - var d = 1 / ( e[3] * x + e[7] * y + e[11] * z + e[15] ); // perspective divide - - this.x = ( e[0] * x + e[4] * y + e[8] * z + e[12] ) * d; - this.y = ( e[1] * x + e[5] * y + e[9] * z + e[13] ) * d; - this.z = ( e[2] * x + e[6] * y + e[10] * z + e[14] ) * d; - - return this; - - }, - - applyQuaternion: function ( q ) { - - var x = this.x; - var y = this.y; - var z = this.z; - - var qx = q.x; - var qy = q.y; - var qz = q.z; - var qw = q.w; - - // calculate quat * vector - - var ix = qw * x + qy * z - qz * y; - var iy = qw * y + qz * x - qx * z; - var iz = qw * z + qx * y - qy * x; - var iw = -qx * x - qy * y - qz * z; - - // calculate result * inverse quat - - this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; - this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; - this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; - - return this; - - }, - - transformDirection: function ( m ) { - - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction - - var x = this.x, y = this.y, z = this.z; - - var e = m.elements; - - this.x = e[0] * x + e[4] * y + e[8] * z; - this.y = e[1] * x + e[5] * y + e[9] * z; - this.z = e[2] * x + e[6] * y + e[10] * z; - - this.normalize(); - - return this; - - }, - - divide: function ( v ) { - - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; - - return this; - - }, - - divideScalar: function ( scalar ) { - - if ( scalar !== 0 ) { - - var invScalar = 1 / scalar; - - this.x *= invScalar; - this.y *= invScalar; - this.z *= invScalar; - - } else { - - this.x = 0; - this.y = 0; - this.z = 0; - - } - - return this; - - }, - - min: function ( v ) { - - if ( this.x > v.x ) { - - this.x = v.x; - - } - - if ( this.y > v.y ) { - - this.y = v.y; - - } - - if ( this.z > v.z ) { - - this.z = v.z; - - } - - return this; - - }, - - max: function ( v ) { - - if ( this.x < v.x ) { - - this.x = v.x; - - } - - if ( this.y < v.y ) { - - this.y = v.y; - - } - - if ( this.z < v.z ) { - - this.z = v.z; - - } - - return this; - - }, - - clamp: function ( min, max ) { - - // This function assumes min < max, if this assumption isn't true it will not operate correctly - - if ( this.x < min.x ) { - - this.x = min.x; - - } else if ( this.x > max.x ) { - - this.x = max.x; - - } - - if ( this.y < min.y ) { - - this.y = min.y; - - } else if ( this.y > max.y ) { - - this.y = max.y; - - } - - if ( this.z < min.z ) { - - this.z = min.z; - - } else if ( this.z > max.z ) { - - this.z = max.z; - - } - - return this; - - }, - - clampScalar: ( function () { - - var min, max; - - return function ( minVal, maxVal ) { - - if ( min === undefined ) { - - min = new cornerstoneMath.Vector3(); - max = new cornerstoneMath.Vector3(); - - } - - min.set( minVal, minVal, minVal ); - max.set( maxVal, maxVal, maxVal ); - - return this.clamp( min, max ); - - }; - - } )(), - - floor: function () { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - - return this; - - }, - - ceil: function () { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - - return this; - - }, - - round: function () { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - - return this; - - }, - - roundToZero: function () { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - - return this; - - }, - - negate: function () { - - return this.multiplyScalar( - 1 ); - - }, - - dot: function ( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z; - - }, - - lengthSq: function () { - - return this.x * this.x + this.y * this.y + this.z * this.z; - - }, - - length: function () { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - - }, - - lengthManhattan: function () { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - - }, - - normalize: function () { - - return this.divideScalar( this.length() ); - - }, - - setLength: function ( l ) { - - var oldLength = this.length(); - - if ( oldLength !== 0 && l !== oldLength ) { - - this.multiplyScalar( l / oldLength ); - } - - return this; - - }, - - lerp: function ( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - - return this; - - }, - - cross: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'DEPRECATED: Vector3\'s .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); - - } - - var x = this.x, y = this.y, z = this.z; - - this.x = y * v.z - z * v.y; - this.y = z * v.x - x * v.z; - this.z = x * v.y - y * v.x; - - return this; - - }, - - crossVectors: function ( a, b ) { - - var ax = a.x, ay = a.y, az = a.z; - var bx = b.x, by = b.y, bz = b.z; - - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; - - return this; - - }, - - projectOnVector: function () { - - var v1, dot; - - return function ( vector ) { - - if ( v1 === undefined ) v1 = new cornerstoneMath.Vector3(); - - v1.copy( vector ).normalize(); - - dot = this.dot( v1 ); - - return this.copy( v1 ).multiplyScalar( dot ); - - }; - - }(), - - projectOnPlane: function () { - - var v1; - - return function ( planeNormal ) { - - if ( v1 === undefined ) v1 = new cornerstoneMath.Vector3(); - - v1.copy( this ).projectOnVector( planeNormal ); - - return this.sub( v1 ); - - }; - - }(), - - reflect: function () { - - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length - - var v1; - - return function ( normal ) { - - if ( v1 === undefined ) v1 = new cornerstoneMath.Vector3(); - - return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - - }; - - }(), - - angleTo: function ( v ) { - - var theta = this.dot( v ) / ( this.length() * v.length() ); - - // clamp, to handle numerical problems - - return Math.acos( cornerstoneMath.clamp( theta, -1, 1 ) ); - - }, - - distanceTo: function ( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - }, - - distanceToSquared: function ( v ) { - - var dx = this.x - v.x; - var dy = this.y - v.y; - var dz = this.z - v.z; - - return dx * dx + dy * dy + dz * dz; - - }, - - setEulerFromRotationMatrix: function ( m, order ) { - - console.error( "REMOVED: Vector3\'s setEulerFromRotationMatrix has been removed in favor of Euler.setFromRotationMatrix(), please update your code."); - - }, - - setEulerFromQuaternion: function ( q, order ) { - - console.error( "REMOVED: Vector3\'s setEulerFromQuaternion: has been removed in favor of Euler.setFromQuaternion(), please update your code."); - - }, - - getPositionFromMatrix: function ( m ) { - - console.warn( "DEPRECATED: Vector3\'s .getPositionFromMatrix() has been renamed to .setFromMatrixPosition(). Please update your code." ); - - return this.setFromMatrixPosition( m ); - - }, - - getScaleFromMatrix: function ( m ) { - - console.warn( "DEPRECATED: Vector3\'s .getScaleFromMatrix() has been renamed to .setFromMatrixScale(). Please update your code." ); - - return this.setFromMatrixScale( m ); - }, - - getColumnFromMatrix: function ( index, matrix ) { - - console.warn( "DEPRECATED: Vector3\'s .getColumnFromMatrix() has been renamed to .setFromMatrixColumn(). Please update your code." ); - - return this.setFromMatrixColumn( index, matrix ); - - }, - - setFromMatrixPosition: function ( m ) { - - this.x = m.elements[ 12 ]; - this.y = m.elements[ 13 ]; - this.z = m.elements[ 14 ]; - - return this; - - }, - - setFromMatrixScale: function ( m ) { - - var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); - var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); - var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); - - this.x = sx; - this.y = sy; - this.z = sz; - - return this; - }, - - setFromMatrixColumn: function ( index, matrix ) { - - var offset = index * 4; - - var me = matrix.elements; - - this.x = me[ offset ]; - this.y = me[ offset + 1 ]; - this.z = me[ offset + 2 ]; - - return this; - - }, - - equals: function ( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - - }, - - fromArray: function ( array ) { - - this.x = array[ 0 ]; - this.y = array[ 1 ]; - this.z = array[ 2 ]; - - return this; - - }, - - toArray: function () { - - return [ this.x, this.y, this.z ]; - - }, - - clone: function () { - - return new cornerstoneMath.Vector3( this.x, this.y, this.z ); - - } - - }; - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/vector3.js - -// Begin Source: src/Line3.js -// Copied from THREE.JS -/** - * @author bhouston / http://exocortex.com - */ - - -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if (cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - cornerstoneMath.Line3 = function ( start, end ) { - - this.start = ( start !== undefined ) ? start : new cornerstoneMath.Vector3(); - this.end = ( end !== undefined ) ? end : new cornerstoneMath.Vector3(); - - }; - - cornerstoneMath.Line3.prototype = { - - constructor: cornerstoneMath.Line3, - - set: function ( start, end ) { - - this.start.copy( start ); - this.end.copy( end ); - - return this; - - }, - - copy: function ( line ) { - - this.start.copy( line.start ); - this.end.copy( line.end ); - - return this; - - }, - - center: function ( optionalTarget ) { - - var result = optionalTarget || new cornerstoneMath.Vector3(); - return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); - - }, - - delta: function ( optionalTarget ) { - - var result = optionalTarget || new cornerstoneMath.Vector3(); - return result.subVectors( this.end, this.start ); - - }, - - distanceSq: function () { - - return this.start.distanceToSquared( this.end ); - - }, - - distance: function () { - - return this.start.distanceTo( this.end ); - - }, - - at: function ( t, optionalTarget ) { - - var result = optionalTarget || new cornerstoneMath.Vector3(); - - return this.delta( result ).multiplyScalar( t ).add( this.start ); - - }, - - closestPointToPointParameter: function () { - - var startP = new cornerstoneMath.Vector3(); - var startEnd = new cornerstoneMath.Vector3(); - - return function ( point, clampToLine ) { - - startP.subVectors( point, this.start ); - startEnd.subVectors( this.end, this.start ); - - var startEnd2 = startEnd.dot( startEnd ); - var startEnd_startP = startEnd.dot( startP ); - - var t = startEnd_startP / startEnd2; - - if ( clampToLine ) { - - t = cornerstoneMath.Math.clamp( t, 0, 1 ); - - } - - return t; - - }; - - }(), - - closestPointToPoint: function ( point, clampToLine, optionalTarget ) { - - var t = this.closestPointToPointParameter( point, clampToLine ); - - var result = optionalTarget || new cornerstoneMath.Vector3(); - - return this.delta( result ).multiplyScalar( t ).add( this.start ); - - }, - - applyMatrix4: function ( matrix ) { - - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); - - return this; - - }, - - equals: function ( line ) { - - return line.start.equals( this.start ) && line.end.equals( this.end ); - - }, - - clone: function () { - - return new cornerstoneMath.Line3().copy( this ); - - }, - - intersectLine: function ( line ) { - // http://stackoverflow.com/questions/2316490/the-algorithm-to-find-the-point-of-intersection-of-two-3d-line-segment/10288710#10288710 - var da = this.end.clone().sub(this.start); - var db = line.end.clone().sub(line.start); - var dc = line.start.clone().sub(this.start); - - var daCrossDb = da.clone().cross(db); - var dcCrossDb = dc.clone().cross(db); - - if (dc.dot(da) === 0){ - // Lines are not coplanar, stop here - return; - } - - var s = dcCrossDb.dot(daCrossDb) / daCrossDb.lengthSq(); - - // Make sure we have an intersection - if (s > 1.0 || isNaN(s)) { - return; - } - - var intersection = this.start.clone().add(da.clone().multiplyScalar(s)); - var distanceTest = intersection.clone().sub(line.start).lengthSq() + intersection.clone().sub(line.end).lengthSq(); - if (distanceTest <= line.distanceSq()) { - return intersection; - } - return; - } - }; - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/Line3.js - -// Begin Source: src/lineSegment.js -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if(cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - // based on http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment - function sqr(x) - { - return x * x; - } - - function dist2(v, w) { - return sqr(v.x - w.x) + sqr(v.y - w.y); - } - - function distanceToPointSquared(lineSegment, point) - { - var l2 = dist2(lineSegment.start, lineSegment.end); - if(l2 === 0) { - return dist2(point, lineSegment.start); - } - var t = ((point.x - lineSegment.start.x) * (lineSegment.end.x - lineSegment.start.x) + - (point.y - lineSegment.start.y) * (lineSegment.end.y - lineSegment.start.y)) / l2; - if(t < 0) { - return dist2(point, lineSegment.start); - } - if(t > 1) { - return dist2(point, lineSegment.end); - } - - var pt = { - x : lineSegment.start.x + t * (lineSegment.end.x - lineSegment.start.x), - y : lineSegment.start.y + t * (lineSegment.end.y - lineSegment.start.y) - }; - return dist2(point, pt); - } - - function distanceToPoint(lineSegment, point) - { - return Math.sqrt(distanceToPointSquared(lineSegment, point)); - } - - // Returns intersection points of two lines - function intersectLine(lineSegment1, lineSegment2) { - var intersectionPoint = {}; - - var x1 = lineSegment1.start.x, y1 = lineSegment1.start.y, x2 = lineSegment1.end.x, y2 = lineSegment1.end.y, - x3 = lineSegment2.start.x, y3 = lineSegment2.start.y, x4 = lineSegment2.end.x, y4 = lineSegment2.end.y; - - var a1, a2, b1, b2, c1, c2; // Coefficients of line equations - var r1, r2, r3, r4; // Sign values - - var denom, num; //Intermediate values - - // Compute a1, b1, c1, where line joining points 1 and 2 is "a1 x + b1 y + c1 = 0" - a1 = y2 - y1; - b1 = x1 - x2; - c1 = x2 * y1 - x1 * y2; - - // Compute r3 and r4 - r3 = a1 * x3 + b1 * y3 + c1; - r4 = a1 * x4 + b1 * y4 + c1; - - /* Check signs of r3 and r4. If both point 3 and point 4 lie on - * same side of line 1, the line segments do not intersect. - */ - - if (r3 !== 0 && - r4 !== 0 && - cornerstoneMath.sign(r3) === cornerstoneMath.sign(r4)) { - return; - } - - /* Compute a2, b2, c2 */ - - a2 = y4 - y3; - b2 = x3 - x4; - c2 = x4 * y3 - x3 * y4; - - /* Compute r1 and r2 */ - - r1 = a2 * x1 + b2 * y1 + c2; - r2 = a2 * x2 + b2 * y2 + c2; - - /* Check signs of r1 and r2. If both point 1 and point 2 lie - * on same side of second line segment, the line segments do - * not intersect. - */ - - if (r1 !== 0 && - r2 !== 0 && - cornerstoneMath.sign(r1) === cornerstoneMath.sign(r2)) { - return; - } - - /* Line segments intersect: compute intersection point. - */ - - denom = (a1 * b2) - (a2 * b1); - - /* The denom/2 is to get rounding instead of truncating. It - * is added or subtracted to the numerator, depending upon the - * sign of the numerator. - */ - - num = (b1 * c2) - (b2 * c1); - var x = parseFloat(num / denom); - - num = (a2 * c1) - (a1 * c2); - var y = parseFloat(num / denom); - - intersectionPoint.x = x; - intersectionPoint.y = y; - - return intersectionPoint; - } - - // module exports - cornerstoneMath.lineSegment = - { - distanceToPoint : distanceToPoint, - intersectLine: intersectLine - }; - - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/lineSegment.js - -// Begin Source: src/math.js -// Based on THREE.JS - -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if (cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - function clamp(x,a,b) { - return ( x < a ) ? a : ( ( x > b ) ? b : x ); - } - - function degToRad(degrees) { - var degreeToRadiansFactor = Math.PI / 180; - return degrees * degreeToRadiansFactor; - } - - function radToDeg(radians) { - var radianToDegreesFactor = 180 / Math.PI; - return radians * radianToDegreesFactor; - } - - // Returns sign of number - function sign(x) { - return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN; - } - - cornerstoneMath.clamp = clamp; - cornerstoneMath.degToRad = degToRad; - cornerstoneMath.radToDeg = radToDeg; - cornerstoneMath.sign = sign; - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/math.js - -// Begin Source: src/matrix4.js -// Based on THREE.JS - -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if(cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - cornerstoneMath.Matrix4 = function Matrix4(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) - { - this.elements = new Float32Array( 16 ); - - // TODO: if n11 is undefined, then just set to identity, otherwise copy all other values into matrix - // we should not support semi specification of Matrix4, it is just weird. - - var te = this.elements; - - te[0] = ( n11 !== undefined ) ? n11 : 1; te[4] = n12 || 0; te[8] = n13 || 0; te[12] = n14 || 0; - te[1] = n21 || 0; te[5] = ( n22 !== undefined ) ? n22 : 1; te[9] = n23 || 0; te[13] = n24 || 0; - te[2] = n31 || 0; te[6] = n32 || 0; te[10] = ( n33 !== undefined ) ? n33 : 1; te[14] = n34 || 0; - te[3] = n41 || 0; te[7] = n42 || 0; te[11] = n43 || 0; te[15] = ( n44 !== undefined ) ? n44 : 1; - }; - - cornerstoneMath.Matrix4.prototype.makeRotationFromQuaternion = function(q) { - var te = this.elements; - - var x = q.x, y = q.y, z = q.z, w = q.w; - var x2 = x + x, y2 = y + y, z2 = z + z; - var xx = x * x2, xy = x * y2, xz = x * z2; - var yy = y * y2, yz = y * z2, zz = z * z2; - var wx = w * x2, wy = w * y2, wz = w * z2; - - te[0] = 1 - ( yy + zz ); - te[4] = xy - wz; - te[8] = xz + wy; - - te[1] = xy + wz; - te[5] = 1 - ( xx + zz ); - te[9] = yz - wx; - - te[2] = xz - wy; - te[6] = yz + wx; - te[10] = 1 - ( xx + yy ); - - // last column - te[3] = 0; - te[7] = 0; - te[11] = 0; - - // bottom row - te[12] = 0; - te[13] = 0; - te[14] = 0; - te[15] = 1; - - return this; - }; - - cornerstoneMath.Matrix4.prototype.multiplyMatrices = function(a, b) - { - var ae = a.elements; - var be = b.elements; - var te = this.elements; - - var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; - var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; - var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; - var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; - - var b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; - var b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; - var b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; - var b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; - - te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; - te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; - te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; - te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; - - te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; - te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; - te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; - te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; - - te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; - te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; - te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; - te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; - - te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; - te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; - te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; - te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; - - return this; - }; - - cornerstoneMath.Matrix4.prototype.multiply = function(m, n ) - { - - if (n !== undefined) { - - console.warn('DEPRECATED: Matrix4\'s .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.'); - return this.multiplyMatrices(m, n); - } - - return this.multiplyMatrices(this, m); - }; - - cornerstoneMath.Matrix4.prototype.getInverse = function ( m, throwOnInvertible ) { - - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - var te = this.elements; - var me = m.elements; - - var n11 = me[0], n12 = me[4], n13 = me[8], n14 = me[12]; - var n21 = me[1], n22 = me[5], n23 = me[9], n24 = me[13]; - var n31 = me[2], n32 = me[6], n33 = me[10], n34 = me[14]; - var n41 = me[3], n42 = me[7], n43 = me[11], n44 = me[15]; - - te[0] = n23*n34*n42 - n24*n33*n42 + n24*n32*n43 - n22*n34*n43 - n23*n32*n44 + n22*n33*n44; - te[4] = n14*n33*n42 - n13*n34*n42 - n14*n32*n43 + n12*n34*n43 + n13*n32*n44 - n12*n33*n44; - te[8] = n13*n24*n42 - n14*n23*n42 + n14*n22*n43 - n12*n24*n43 - n13*n22*n44 + n12*n23*n44; - te[12] = n14*n23*n32 - n13*n24*n32 - n14*n22*n33 + n12*n24*n33 + n13*n22*n34 - n12*n23*n34; - te[1] = n24*n33*n41 - n23*n34*n41 - n24*n31*n43 + n21*n34*n43 + n23*n31*n44 - n21*n33*n44; - te[5] = n13*n34*n41 - n14*n33*n41 + n14*n31*n43 - n11*n34*n43 - n13*n31*n44 + n11*n33*n44; - te[9] = n14*n23*n41 - n13*n24*n41 - n14*n21*n43 + n11*n24*n43 + n13*n21*n44 - n11*n23*n44; - te[13] = n13*n24*n31 - n14*n23*n31 + n14*n21*n33 - n11*n24*n33 - n13*n21*n34 + n11*n23*n34; - te[2] = n22*n34*n41 - n24*n32*n41 + n24*n31*n42 - n21*n34*n42 - n22*n31*n44 + n21*n32*n44; - te[6] = n14*n32*n41 - n12*n34*n41 - n14*n31*n42 + n11*n34*n42 + n12*n31*n44 - n11*n32*n44; - te[10] = n12*n24*n41 - n14*n22*n41 + n14*n21*n42 - n11*n24*n42 - n12*n21*n44 + n11*n22*n44; - te[14] = n14*n22*n31 - n12*n24*n31 - n14*n21*n32 + n11*n24*n32 + n12*n21*n34 - n11*n22*n34; - te[3] = n23*n32*n41 - n22*n33*n41 - n23*n31*n42 + n21*n33*n42 + n22*n31*n43 - n21*n32*n43; - te[7] = n12*n33*n41 - n13*n32*n41 + n13*n31*n42 - n11*n33*n42 - n12*n31*n43 + n11*n32*n43; - te[11] = n13*n22*n41 - n12*n23*n41 - n13*n21*n42 + n11*n23*n42 + n12*n21*n43 - n11*n22*n43; - te[15] = n12*n23*n31 - n13*n22*n31 + n13*n21*n32 - n11*n23*n32 - n12*n21*n33 + n11*n22*n33; - - var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ]; - - if ( det === 0 ) { - - var msg = "Matrix4.getInverse(): can't invert matrix, determinant is 0"; - - if ( throwOnInvertible || false ) { - - throw new Error( msg ); - - } else { - - console.warn( msg ); - - } - - this.identity(); - - return this; - } - - this.multiplyScalar( 1 / det ); - - return this; - - }; - - cornerstoneMath.Matrix4.prototype.applyToVector3Array = function() { - - var v1 = new cornerstoneMath.Vector3(); - - return function ( array, offset, length ) { - - if ( offset === undefined ) offset = 0; - if ( length === undefined ) length = array.length; - - for ( var i = 0, j = offset, il; i < length; i += 3, j += 3 ) { - - v1.x = array[ j ]; - v1.y = array[ j + 1 ]; - v1.z = array[ j + 2 ]; - - v1.applyMatrix4( this ); - - array[ j ] = v1.x; - array[ j + 1 ] = v1.y; - array[ j + 2 ] = v1.z; - - } - - return array; - - }; - - }; - - cornerstoneMath.Matrix4.prototype.makeTranslation = function ( x, y, z ) { - - this.set( - - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 - - ); - - return this; - - }; - cornerstoneMath.Matrix4.prototype.multiplyScalar = function ( s ) { - - var te = this.elements; - - te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; - te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; - te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; - te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; - - return this; - - }; - cornerstoneMath.Matrix4.prototype.set = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - - var te = this.elements; - - te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; - te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; - te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; - te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; - - return this; - - }; - - cornerstoneMath.Matrix4.prototype.makeScale = function ( x, y, z ) { - - this.set( - - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 - - ); - - return this; - - }; - - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/matrix4.js - -// Begin Source: src/plane.js -// Copied from Three.JS -/** - * @author bhouston / http://exocortex.com - */ - -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if (cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - cornerstoneMath.Plane = function ( normal, constant ) { - - this.normal = ( normal !== undefined ) ? normal : new cornerstoneMath.Vector3( 1, 0, 0 ); - this.constant = ( constant !== undefined ) ? constant : 0; - - }; - - cornerstoneMath.Plane.prototype = { - - constructor: cornerstoneMath.Plane, - - set: function ( normal, constant ) { - - this.normal.copy( normal ); - this.constant = constant; - - return this; - - }, - - setComponents: function ( x, y, z, w ) { - - this.normal.set( x, y, z ); - this.constant = w; - - return this; - - }, - - setFromNormalAndCoplanarPoint: function ( normal, point ) { - - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized - - return this; - - }, - - setFromCoplanarPoints: function () { - - var v1 = new cornerstoneMath.Vector3(); - var v2 = new cornerstoneMath.Vector3(); - - return function ( a, b, c ) { - - var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); - - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - - this.setFromNormalAndCoplanarPoint( normal, a ); - - return this; - - }; - - }(), - - - copy: function ( plane ) { - - this.normal.copy( plane.normal ); - this.constant = plane.constant; - - return this; - - }, - - normalize: function () { - - // Note: will lead to a divide by zero if the plane is invalid. - - var inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; - - return this; - - }, - - negate: function () { - - this.constant *= - 1; - this.normal.negate(); - - return this; - - }, - - distanceToPoint: function ( point ) { - - return this.normal.dot( point ) + this.constant; - - }, - - distanceToSphere: function ( sphere ) { - - return this.distanceToPoint( sphere.center ) - sphere.radius; - - }, - - projectPoint: function ( point, optionalTarget ) { - - return this.orthoPoint( point, optionalTarget ).sub( point ).negate(); - - }, - - orthoPoint: function ( point, optionalTarget ) { - - var perpendicularMagnitude = this.distanceToPoint( point ); - - var result = optionalTarget || new cornerstoneMath.Vector3(); - return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude ); - - }, - - isIntersectionLine: function ( line ) { - - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - - var startSign = this.distanceToPoint( line.start ); - var endSign = this.distanceToPoint( line.end ); - - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - - }, - - intersectLine: function () { - - var v1 = new cornerstoneMath.Vector3(); - - return function ( line, optionalTarget ) { - - var result = optionalTarget || new cornerstoneMath.Vector3(); - - var direction = line.delta( v1 ); - - var denominator = this.normal.dot( direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { - - return result.copy( line.start ); - - } - - // Unsure if this is the correct method to handle this case. - return undefined; - - } - - var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - - if ( t < 0 || t > 1 ) { - - return undefined; - - } - - return result.copy( direction ).multiplyScalar( t ).add( line.start ); - - }; - - }(), - - intersectPlane: function (targetPlane) { - // Returns the intersection line between two planes - var direction = this.normal.clone().cross(targetPlane.normal); - var origin = new cornerstoneMath.Vector3(); - var intersectionData = { - origin: origin, - direction: direction - }; - - // If the planes are parallel, return an empty vector for the - // intersection line - if (this.normal.clone().cross(targetPlane.normal).length < 1e-10) { - intersectionData.direction = new cornerstoneMath.Vector3(); - return intersectionData; - } - - var h1 = this.constant; - var h2 = targetPlane.constant; - var n1dotn2 = this.normal.clone().dot(targetPlane.normal); - - var c1 = -(h1 - h2 * n1dotn2) / (1 - n1dotn2 * n1dotn2); - var c2 = -(h2 - h1 * n1dotn2) / (1 - n1dotn2 * n1dotn2); - intersectionData.origin = this.normal.clone().multiplyScalar(c1).add(targetPlane.normal.clone().multiplyScalar(c2)); - return intersectionData; - }, - - coplanarPoint: function ( optionalTarget ) { - - var result = optionalTarget || new cornerstoneMath.Vector3(); - return result.copy( this.normal ).multiplyScalar( - this.constant ); - - }, - - translate: function ( offset ) { - - this.constant = this.constant - offset.dot( this.normal ); - - return this; - - }, - - equals: function ( plane ) { - - return plane.normal.equals( this.normal ) && ( plane.constant == this.constant ); - - }, - - clone: function () { - - return new cornerstoneMath.Plane().copy( this ); - - } - }; - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/plane.js - -// Begin Source: src/point.js -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if(cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - function pageToPoint(e) - { - return { - x : e.pageX, - y : e.pageY - }; - } - - function subtract(lhs, rhs) - { - return { - x : lhs.x - rhs.x, - y : lhs.y - rhs.y - }; - } - - function copy(point) - { - return { - x : point.x, - y : point.y - }; - } - - function distance(from, to) - { - return Math.sqrt(distanceSquared(from, to)); - } - - function distanceSquared(from, to) - { - var delta = subtract(from, to); - return delta.x * delta.x + delta.y * delta.y; - } - - function insideRect(point, rect) - { - if( point.x < rect.left || - point.x > rect.left + rect.width || - point.y < rect.top || - point.y > rect.top + rect.height) - { - return false; - } - return true; - } - - /** - * Returns the closest source point to a target point - * given an array of source points. - * - * @param sources An Array of source Points - * @param target The target Point - * @returns Point The closest point from the points array - */ - function findClosestPoint(sources, target) { - var distances = []; - var minDistance; - sources.forEach(function(source, index) { - var distance = cornerstoneMath.point.distance(source, target); - distances.push(distance); - - if (index === 0) { - minDistance = distance; - } else { - minDistance = Math.min(distance, minDistance); - } - }); - - var index = distances.indexOf(minDistance); - return sources[index]; - } - - // module exports - cornerstoneMath.point = - { - subtract : subtract, - copy: copy, - pageToPoint: pageToPoint, - distance: distance, - distanceSquared: distanceSquared, - insideRect: insideRect, - findClosestPoint: findClosestPoint - }; - - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/point.js - -// Begin Source: src/quaternion.js -// Based on THREE.JS - -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if(cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - cornerstoneMath.Quaternion = function Quaternion(x, y, z, w) - { - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - this.w = ( w !== undefined ) ? w : 1; - }; - - cornerstoneMath.Quaternion.prototype.setFromAxisAngle = function(axis, angle) - { - var halfAngle = angle / 2, s = Math.sin( halfAngle ); - - this.x = axis.x * s; - this.y = axis.y * s; - this.z = axis.z * s; - this.w = Math.cos( halfAngle ); - - return this; - }; - - cornerstoneMath.Quaternion.prototype.multiplyQuaternions = function( a, b) - { - var qax = a.x, qay = a.y, qaz = a.z, qaw = a.w; - var qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w; - - this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - - return this; - }; - - cornerstoneMath.Quaternion.prototype.setFromRotationMatrix = function(m) - { - var te = m.elements, - - m11 = te[0], m12 = te[4], m13 = te[8], - m21 = te[1], m22 = te[5], m23 = te[9], - m31 = te[2], m32 = te[6], m33 = te[10], - - trace = m11 + m22 + m33, - s; - - if ( trace > 0 ) { - - s = 0.5 / Math.sqrt( trace + 1.0 ); - - this.w = 0.25 / s; - this.x = ( m32 - m23 ) * s; - this.y = ( m13 - m31 ) * s; - this.z = ( m21 - m12 ) * s; - - } else if ( m11 > m22 && m11 > m33 ) { - - s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - - this.w = (m32 - m23 ) / s; - this.x = 0.25 * s; - this.y = (m12 + m21 ) / s; - this.z = (m13 + m31 ) / s; - - } else if ( m22 > m33 ) { - - s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - - this.w = (m13 - m31 ) / s; - this.x = (m12 + m21 ) / s; - this.y = 0.25 * s; - this.z = (m23 + m32 ) / s; - - } else { - - s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - - this.w = ( m21 - m12 ) / s; - this.x = ( m13 + m31 ) / s; - this.y = ( m23 + m32 ) / s; - this.z = 0.25 * s; - - } - - return this; - }; - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/quaternion.js - -// Begin Source: src/rect.js -var cornerstoneMath = (function (cornerstoneMath) { - - "use strict"; - - if(cornerstoneMath === undefined) { - cornerstoneMath = {}; - } - - function rectToLineSegments(rect) - { - var top = { - start : { - x :rect.left, - y :rect.top - }, - end : { - x :rect.left + rect.width, - y :rect.top - - } - }; - var right = { - start : { - x :rect.left + rect.width, - y :rect.top - }, - end : { - x :rect.left + rect.width, - y :rect.top + rect.height - - } - }; - var bottom = { - start : { - x :rect.left + rect.width, - y :rect.top + rect.height - }, - end : { - x :rect.left, - y :rect.top + rect.height - - } - }; - var left = { - start : { - x :rect.left, - y :rect.top + rect.height - }, - end : { - x :rect.left, - y :rect.top - - } - }; - var lineSegments = [top, right, bottom, left]; - return lineSegments; - } - - function pointNearLineSegment(point, lineSegment, maxDistance) - { - if(maxDistance === undefined) { - maxDistance = 5; - } - var distance = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, point); - - return (distance < maxDistance); - } - function distanceToPoint(rect, point) - { - var minDistance = 655535; - var lineSegments = rectToLineSegments(rect); - lineSegments.forEach(function(lineSegment) { - var distance = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, point); - if(distance < minDistance) { - minDistance = distance; - } - }); - return minDistance; - } - - // Returns top-left and bottom-right points of the rectangle - function rectToPoints (rect) { - var rectPoints = { - topLeft: { - x: rect.left, - y: rect.top - }, - bottomRight: { - x: rect.left + rect.width, - y: rect.top + rect.height - } - }; - - return rectPoints; - } - - // Returns whether two non-rotated rectangles are intersected - function doesIntersect (rect1, rect2) { - var intersectLeftRight; - var intersectTopBottom; - - var rect1Points = rectToPoints(rect1); - var rect2Points = rectToPoints(rect2); - - if (rect1.width >= 0) { - if (rect2.width >= 0) - intersectLeftRight = !((rect1Points.bottomRight.x <= rect2Points.topLeft.x) || (rect2Points.bottomRight.x <= rect1Points.topLeft.x)); - else - intersectLeftRight = !((rect1Points.bottomRight.x <= rect2Points.bottomRight.x) || (rect2Points.topLeft.x <= rect1Points.topLeft.x)); - } else { - if (rect2.width >= 0) - intersectLeftRight = !((rect1Points.topLeft.x <= rect2Points.topLeft.x) || (rect2Points.bottomRight.x <= rect1Points.bottomRight.x)); - else - intersectLeftRight = !((rect1Points.topLeft.x <= rect2Points.bottomRight.x) || (rect2Points.topLeft.x <= rect1Points.bottomRight.x)); - } - - if (rect1.height >= 0) { - if (rect2.height >= 0) - intersectTopBottom = !((rect1Points.bottomRight.y <= rect2Points.topLeft.y) || (rect2Points.bottomRight.y <= rect1Points.topLeft.y)); - else - intersectTopBottom = !((rect1Points.bottomRight.y <= rect2Points.bottomRight.y ) || (rect2Points.topLeft.y <= rect1Points.topLeft.y)); - } else { - if (rect2.height >= 0) - intersectTopBottom = !((rect1Points.topLeft.y <= rect2Points.topLeft.y) || (rect2Points.bottomRight.y <= rect1Points.bottomRight.y )); - else - intersectTopBottom = !((rect1Points.topLeft.y <= rect2Points.bottomRight.y ) || (rect2Points.top <= rect1Points.bottomRight.y )); - } - - return intersectLeftRight && intersectTopBottom; - } - - // Returns intersection points of two non-rotated rectangles - function getIntersectionRect(rect1, rect2) { - var intersectRect = { - topLeft: {}, - bottomRight: {} - }; - - if (!doesIntersect(rect1, rect2)) { - return; - } - - var rect1Points = rectToPoints(rect1); - var rect2Points = rectToPoints(rect2); - - if (rect1.width >= 0) { - if (rect2.width >= 0) { - intersectRect.topLeft.x = Math.max(rect1Points.topLeft.x, rect2Points.topLeft.x); - intersectRect.bottomRight.x = Math.min(rect1Points.bottomRight.x, rect2Points.bottomRight.x); - } else { - intersectRect.topLeft.x = Math.max(rect1Points.topLeft.x, rect2Points.bottomRight.x); - intersectRect.bottomRight.x = Math.min(rect1Points.bottomRight.x, rect2Points.topLeft.x); - } - } else { - if (rect2.width >= 0) { - intersectRect.topLeft.x = Math.min(rect1Points.topLeft.x, rect2Points.bottomRight.x); - intersectRect.bottomRight.x = Math.max(rect1Points.bottomRight.x, rect2Points.topLeft.x); - } else { - intersectRect.topLeft.x = Math.min(rect1Points.topLeft.x, rect2Points.topLeft.x); - intersectRect.bottomRight.x = Math.max(rect1Points.bottomRight.x, rect2Points.bottomRight.x); - } - } - - if (rect1.height >= 0) { - if (rect2.height >= 0) { - intersectRect.topLeft.y = Math.max(rect1Points.topLeft.y, rect2Points.topLeft.y); - intersectRect.bottomRight.y = Math.min(rect1Points.bottomRight.y, rect2Points.bottomRight.y); - } else { - intersectRect.topLeft.y = Math.max(rect1Points.topLeft.y, rect2Points.bottomRight.y); - intersectRect.bottomRight.y = Math.min(rect1Points.bottomRight.y, rect2Points.topLeft.y); - } - } else { - if (rect2.height >= 0) { - intersectRect.topLeft.y = Math.min(rect1Points.topLeft.y, rect2Points.bottomRight.y); - intersectRect.bottomRight.y = Math.max(rect1Points.bottomRight.y, rect2Points.topLeft.y); - } else { - intersectRect.topLeft.y = Math.min(rect1Points.topLeft.y, rect2Points.topLeft.y); - intersectRect.bottomRight.y = Math.max(rect1Points.bottomRight.y, rect2Points.bottomRight.y); - } - } - - // Returns top-left and bottom-right points of intersected rectangle - return intersectRect; - - } - - // module exports - cornerstoneMath.rect = - { - distanceToPoint : distanceToPoint, - getIntersectionRect : getIntersectionRect - - }; - - - return cornerstoneMath; -}(cornerstoneMath)); -// End Source; src/rect.js diff --git a/Packages/ohif-cornerstone/client/cornerstoneTools.js b/Packages/ohif-cornerstone/client/cornerstoneTools.js deleted file mode 100644 index 7e0cfb486..000000000 --- a/Packages/ohif-cornerstone/client/cornerstoneTools.js +++ /dev/null @@ -1,12233 +0,0 @@ -/*! cornerstoneTools - v0.8.4 - 2017-04-23 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */ -// Begin Source: src/header.js -if (typeof cornerstone === 'undefined') { - cornerstone = {}; -} - -if (typeof cornerstoneMath === 'undefined') { - cornerstoneMath = {}; -} - -if (typeof dicomParser === 'undefined') { - dicomParser = {}; -} - -if (typeof cornerstoneTools === 'undefined') { - cornerstoneTools = { - referenceLines: {}, - orientation: {} - }; -} - -// End Source; src/header.js - -// Begin Source: src/inputSources/mouseWheelInput.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function mouseWheel(e) { - // !!!HACK/NOTE/WARNING!!! - // for some reason I am getting mousewheel and DOMMouseScroll events on my - // mac os x mavericks system when middle mouse button dragging. - // I couldn't find any info about this so this might break other systems - // webkit hack - if (e.originalEvent.type === 'mousewheel' && e.originalEvent.wheelDeltaY === 0) { - return; - } - // firefox hack - if (e.originalEvent.type === 'DOMMouseScroll' && e.originalEvent.axis === 1) { - return; - } - - e.preventDefault(); - - var element = e.currentTarget; - - var x; - var y; - - if (e.pageX !== undefined && e.pageY !== undefined) { - x = e.pageX; - y = e.pageY; - } else if (e.originalEvent && - e.originalEvent.pageX !== undefined && - e.originalEvent.pageY !== undefined) { - x = e.originalEvent.pageX; - y = e.originalEvent.pageY; - } else { - // IE9 & IE10 - x = e.x; - y = e.y; - } - - var startingCoords = cornerstone.pageToPixel(element, x, y); - - e = window.event || e; // old IE support - - var wheelDelta; - if (e.originalEvent && e.originalEvent.wheelDelta) { - wheelDelta = -e.originalEvent.wheelDelta; - } else if (e.originalEvent && e.originalEvent.deltaY) { - wheelDelta = -e.originalEvent.deltaY; - } else if (e.originalEvent && e.originalEvent.detail) { - wheelDelta = -e.originalEvent.detail; - } else { - wheelDelta = e.wheelDelta; - } - - var direction = wheelDelta < 0 ? -1 : 1; - - var mouseWheelData = { - element: element, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - direction: direction, - pageX: x, - pageY: y, - imageX: startingCoords.x, - imageY: startingCoords.y - }; - - $(element).trigger('CornerstoneToolsMouseWheel', mouseWheelData); - } - - var mouseWheelEvents = 'mousewheel DOMMouseScroll'; - - function enable(element) { - // Prevent handlers from being attached multiple times - disable(element); - - $(element).on(mouseWheelEvents, mouseWheel); - } - - function disable(element) { - $(element).unbind(mouseWheelEvents, mouseWheel); - } - - // module exports - cornerstoneTools.mouseWheelInput = { - enable: enable, - disable: disable - }; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/inputSources/mouseWheelInput.js - -// Begin Source: src/inputSources/mouseInput.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var isClickEvent = true; - var preventClickTimeout; - var clickDelay = 200; - - function preventClickHandler() { - isClickEvent = false; - } - - function activateMouseDown(mouseEventDetail) { - $(mouseEventDetail.element).trigger('CornerstoneToolsMouseDownActivate', mouseEventDetail); - } - - function mouseDoubleClick(e) { - var element = e.currentTarget; - var eventType = 'CornerstoneToolsMouseDoubleClick'; - - var startPoints = { - page: cornerstoneMath.point.pageToPoint(e), - image: cornerstone.pageToPixel(element, e.pageX, e.pageY), - client: { - x: e.clientX, - y: e.clientY - } - }; - startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image); - - var lastPoints = cornerstoneTools.copyPoints(startPoints); - var eventData = { - event: e, - which: e.which, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - lastPoints: lastPoints, - currentPoints: startPoints, - deltaPoints: { - x: 0, - y: 0 - }, - type: eventType - }; - - var event = $.Event(eventType, eventData); - $(eventData.element).trigger(event, eventData); - } - - function mouseDown(e) { - preventClickTimeout = setTimeout(preventClickHandler, clickDelay); - - var element = e.currentTarget; - var eventType = 'CornerstoneToolsMouseDown'; - - // Prevent CornerstoneToolsMouseMove while mouse is down - $(element).off('mousemove', mouseMove); - - var startPoints = { - page: cornerstoneMath.point.pageToPoint(e), - image: cornerstone.pageToPixel(element, e.pageX, e.pageY), - client: { - x: e.clientX, - y: e.clientY - } - }; - startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image); - - var lastPoints = cornerstoneTools.copyPoints(startPoints); - var eventData = { - event: e, - which: e.which, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - lastPoints: lastPoints, - currentPoints: startPoints, - deltaPoints: { - x: 0, - y: 0 - }, - type: eventType - }; - - var event = $.Event(eventType, eventData); - $(eventData.element).trigger(event, eventData); - - if (event.isImmediatePropagationStopped() === false) { - // no tools responded to this event, give the active tool a chance - if (activateMouseDown(eventData) === true) { - return cornerstoneTools.pauseEvent(e); - } - } - - var whichMouseButton = e.which; - - function onMouseMove(e) { - // calculate our current points in page and image coordinates - var eventType = 'CornerstoneToolsMouseDrag'; - var currentPoints = { - page: cornerstoneMath.point.pageToPoint(e), - image: cornerstone.pageToPixel(element, e.pageX, e.pageY), - client: { - x: e.clientX, - y: e.clientY - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image); - - // Calculate delta values in page and image coordinates - var deltaPoints = { - page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page), - image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image), - client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client), - canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas) - }; - - var eventData = { - which: whichMouseButton, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - lastPoints: lastPoints, - currentPoints: currentPoints, - deltaPoints: deltaPoints, - type: eventType - }; - - $(eventData.element).trigger(eventType, eventData); - - // update the last points - lastPoints = cornerstoneTools.copyPoints(currentPoints); - - // prevent left click selection of DOM elements - return cornerstoneTools.pauseEvent(e); - } - - // hook mouseup so we can unbind our event listeners - // when they stop dragging - function onMouseUp(e) { - // Cancel the timeout preventing the click event from triggering - clearTimeout(preventClickTimeout); - - var eventType = 'CornerstoneToolsMouseUp'; - if (isClickEvent) { - eventType = 'CornerstoneToolsMouseClick'; - } - - // calculate our current points in page and image coordinates - var currentPoints = { - page: cornerstoneMath.point.pageToPoint(e), - image: cornerstone.pageToPixel(element, e.pageX, e.pageY), - client: { - x: e.clientX, - y: e.clientY - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image); - - // Calculate delta values in page and image coordinates - var deltaPoints = { - page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page), - image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image), - client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client), - canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas) - }; - - var eventData = { - event: e, - which: whichMouseButton, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - lastPoints: lastPoints, - currentPoints: currentPoints, - deltaPoints: deltaPoints, - type: eventType - }; - - var event = $.Event(eventType, eventData); - $(eventData.element).trigger(event, eventData); - - $(document).off('mousemove', onMouseMove); - $(document).off('mouseup', onMouseUp); - - $(eventData.element).on('mousemove', mouseMove); - - isClickEvent = true; - } - - $(document).on('mousemove', onMouseMove); - $(document).on('mouseup', onMouseUp); - - return cornerstoneTools.pauseEvent(e); - } - - function mouseMove(e) { - var element = e.currentTarget; - var eventType = 'CornerstoneToolsMouseMove'; - - var startPoints = { - page: cornerstoneMath.point.pageToPoint(e), - image: cornerstone.pageToPixel(element, e.pageX, e.pageY), - client: { - x: e.clientX, - y: e.clientY - } - }; - startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image); - - var lastPoints = cornerstoneTools.copyPoints(startPoints); - - var whichMouseButton = e.which; - - // calculate our current points in page and image coordinates - var currentPoints = { - page: cornerstoneMath.point.pageToPoint(e), - image: cornerstone.pageToPixel(element, e.pageX, e.pageY), - client: { - x: e.clientX, - y: e.clientY - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image); - - // Calculate delta values in page and image coordinates - var deltaPoints = { - page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page), - image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image), - client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client), - canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas) - }; - - var eventData = { - which: whichMouseButton, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - lastPoints: lastPoints, - currentPoints: currentPoints, - deltaPoints: deltaPoints, - type: eventType - }; - $(element).trigger(eventType, eventData); - - // update the last points - lastPoints = cornerstoneTools.copyPoints(currentPoints); - } - - function disable(element) { - $(element).off('mousedown', mouseDown); - $(element).off('mousemove', mouseMove); - $(element).off('dblclick', mouseDoubleClick); - } - - function enable(element) { - // Prevent handlers from being attached multiple times - disable(element); - - $(element).on('mousedown', mouseDown); - $(element).on('mousemove', mouseMove); - $(element).on('dblclick', mouseDoubleClick); - } - - // module exports - cornerstoneTools.mouseInput = { - enable: enable, - disable: disable - }; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/inputSources/mouseInput.js - -// Begin Source: src/inputSources/touchInput.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - /*jshint newcap: false */ - - var lastScale = 1.0, - lastRotation = 0.0, - startPoints, - currentPoints, - lastPoints, - deltaPoints, - eventData, - touchStartDelay, - pressDelay = 700, - pressTimeout, - isPress = false, - pressMaxDistance = 5, - pageDistanceMoved, - preventNextPinch = false, - lastDelta; - - function onTouch(e) { - var element = e.currentTarget || e.srcEvent.currentTarget, - event, - eventType; - - // Prevent mouse events from occurring alongside touch events - e.preventDefault(); - - // If more than one finger is placed on the element, stop the press timeout - if ((e.pointers && e.pointers.length > 1) || - (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches.length > 1)) { - isPress = false; - clearTimeout(pressTimeout); - } - - switch (e.type) { - case 'tap': - isPress = false; - clearTimeout(pressTimeout); - - // calculate our current points in page and image coordinates - currentPoints = { - page: cornerstoneMath.point.pageToPoint(e.pointers[0]), - image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY), - client: { - x: e.pointers[0].clientX, - y: e.pointers[0].clientY - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image); - - eventType = 'CornerstoneToolsTap'; - eventData = { - event: e, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - currentPoints: currentPoints, - type: eventType, - isTouchEvent: true - }; - - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - break; - - case 'doubletap': - isPress = false; - clearTimeout(pressTimeout); - - // calculate our current points in page and image coordinates - currentPoints = { - page: cornerstoneMath.point.pageToPoint(e.pointers[0]), - image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY), - client: { - x: e.pointers[0].clientX, - y: e.pointers[0].clientY - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image); - - eventType = 'CornerstoneToolsDoubleTap'; - eventData = { - event: e, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - currentPoints: currentPoints, - type: eventType, - isTouchEvent: true - }; - - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - break; - - case 'pinchstart': - isPress = false; - clearTimeout(pressTimeout); - - lastScale = 1.0; - break; - - case 'pinchmove': - isPress = false; - clearTimeout(pressTimeout); - - if (preventNextPinch === true) { - lastScale = e.scale; - preventNextPinch = false; - break; - } - - var scaleChange = (e.scale - lastScale) / lastScale; - - startPoints = { - page: e.center, - image: cornerstone.pageToPixel(element, e.center.x, e.center.y), - }; - startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image); - - eventType = 'CornerstoneToolsTouchPinch'; - eventData = { - event: e, - startPoints: startPoints, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - direction: e.scale < 1 ? 1 : -1, - scaleChange: scaleChange, - type: eventType, - isTouchEvent: true - }; - - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - - lastScale = e.scale; - break; - - case 'touchstart': - lastScale = 1.0; - - clearTimeout(pressTimeout); - - clearTimeout(touchStartDelay); - touchStartDelay = setTimeout(function() { - startPoints = { - page: cornerstoneMath.point.pageToPoint(e.originalEvent.touches[0]), - image: cornerstone.pageToPixel(element, e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY), - client: { - x: e.originalEvent.touches[0].clientX, - y: e.originalEvent.touches[0].clientY - } - }; - startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image); - - eventType = 'CornerstoneToolsTouchStart'; - if (e.originalEvent.touches.length > 1) { - eventType = 'CornerstoneToolsMultiTouchStart'; - } - - eventData = { - event: e, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - currentPoints: startPoints, - type: eventType, - isTouchEvent: true - }; - - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - - if (event.isImmediatePropagationStopped() === false) { - //isPress = false; - //clearTimeout(pressTimeout); - - // No current tools responded to the drag action. - // Create new tool measurement - eventType = 'CornerstoneToolsTouchStartActive'; - if (e.originalEvent.touches.length > 1) { - eventType = 'CornerstoneToolsMultiTouchStartActive'; - } - - eventData.type = eventType; - $(element).trigger(eventType, eventData); - } - - //console.log(eventType); - lastPoints = cornerstoneTools.copyPoints(startPoints); - }, 50); - - isPress = true; - pageDistanceMoved = 0; - pressTimeout = setTimeout(function() { - if (!isPress) { - return; - } - - currentPoints = { - page: cornerstoneMath.point.pageToPoint(e.originalEvent.touches[0]), - image: cornerstone.pageToPixel(element, e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY), - client: { - x: e.originalEvent.touches[0].clientX, - y: e.originalEvent.touches[0].clientY - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image); - - eventType = 'CornerstoneToolsTouchPress'; - eventData = { - event: e, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - currentPoints: currentPoints, - type: eventType, - isTouchEvent: true - }; - - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - - //console.log(eventType); - }, pressDelay); - break; - - case 'touchend': - lastScale = 1.0; - - isPress = false; - clearTimeout(pressTimeout); - - setTimeout(function() { - startPoints = { - page: cornerstoneMath.point.pageToPoint(e.originalEvent.changedTouches[0]), - image: cornerstone.pageToPixel(element, e.originalEvent.changedTouches[0].pageX, e.originalEvent.changedTouches[0].pageY), - client: { - x: e.originalEvent.changedTouches[0].clientX, - y: e.originalEvent.changedTouches[0].clientY - } - }; - startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image); - - eventType = 'CornerstoneToolsTouchEnd'; - - eventData = { - event: e, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - currentPoints: startPoints, - type: eventType, - isTouchEvent: true - }; - - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - }, 50); - break; - - case 'panmove': - // using the delta-value of HammerJS, because it takes all pointers into account - // this is very important when using panning in combination with pinch-zooming - // but HammerJS' delta is relative to the start of the pan event - // so it needs to be converted to a per-event-delta for CornerstoneTools - var delta = { - x: e.deltaX - lastDelta.x, - y: e.deltaY - lastDelta.y - }; - - lastDelta = { - x: e.deltaX, - y: e.deltaY - }; - - // calculate our current points in page and image coordinates - currentPoints = { - page: { - x: lastPoints.page.x + delta.x, - y: lastPoints.page.y + delta.y - }, - image: cornerstone.pageToPixel(element, lastPoints.page.x + delta.x, lastPoints.page.y + delta.y), - client: { - x: lastPoints.client.x + delta.x, - y: lastPoints.client.y + delta.y - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image); - - // Calculate delta values in page and image coordinates - deltaPoints = { - page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page), - image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image), - client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client), - canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas) - }; - - pageDistanceMoved += Math.sqrt(deltaPoints.page.x * deltaPoints.page.x + deltaPoints.page.y * deltaPoints.page.y); - //console.log("pageDistanceMoved: " + pageDistanceMoved); - if (pageDistanceMoved > pressMaxDistance) { - //console.log('Press event aborted due to movement'); - isPress = false; - clearTimeout(pressTimeout); - } - - eventType = 'CornerstoneToolsTouchDrag'; - if (e.pointers.length > 1) { - eventType = 'CornerstoneToolsMultiTouchDrag'; - } - - eventData = { - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - lastPoints: lastPoints, - currentPoints: currentPoints, - deltaPoints: deltaPoints, - numPointers: e.pointers.length, - type: eventType, - isTouchEvent: true - }; - - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - - lastPoints = cornerstoneTools.copyPoints(currentPoints); - break; - - case 'panstart': - lastDelta = { - x: e.deltaX, - y: e.deltaY - }; - - currentPoints = { - page: cornerstoneMath.point.pageToPoint(e.pointers[0]), - image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY), - client: { - x: e.pointers[0].clientX, - y: e.pointers[0].clientY - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image); - lastPoints = cornerstoneTools.copyPoints(currentPoints); - break; - - case 'panend': - isPress = false; - clearTimeout(pressTimeout); - - // If lastPoints is not yet set, it means panend fired without panstart or pan, - // so we can ignore this event - if (!lastPoints) { - return false; - } - - currentPoints = { - page: cornerstoneMath.point.pageToPoint(e.pointers[0]), - image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY), - client: { - x: e.pointers[0].clientX, - y: e.pointers[0].clientY - } - }; - currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image); - - // Calculate delta values in page and image coordinates - deltaPoints = { - page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page), - image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image), - client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client), - canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas) - }; - - eventType = 'CornerstoneToolsDragEnd'; - - eventData = { - event: e.srcEvent, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - startPoints: startPoints, - lastPoints: lastPoints, - currentPoints: currentPoints, - deltaPoints: deltaPoints, - type: eventType, - isTouchEvent: true - }; - - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - - var remainingPointers = e.pointers.length - e.changedPointers.length; - if (remainingPointers === 2) { - preventNextPinch = true; - } - - return cornerstoneTools.pauseEvent(e); - - case 'rotatemove': - isPress = false; - clearTimeout(pressTimeout); - - var rotation = e.rotation - lastRotation; - lastRotation = e.rotation; - - eventType = 'CornerstoneToolsTouchRotate'; - eventData = { - event: e.srcEvent, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - element: element, - rotation: rotation, - type: eventType - }; - event = $.Event(eventType, eventData); - $(element).trigger(event, eventData); - break; - } - - //console.log(eventType); - return false; - } - - function enable(element) { - disable(element); - - var hammerOptions = { - inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput - }; - - var mc = new Hammer.Manager(element, hammerOptions); - - var panOptions = { - pointers: 0, - direction: Hammer.DIRECTION_ALL, - threshold: 0 - }; - - var pan = new Hammer.Pan(panOptions); - var pinch = new Hammer.Pinch({ - threshold: 0 - }); - var rotate = new Hammer.Rotate({ - threshold: 0 - }); - - // we want to detect both the same time - pinch.recognizeWith(pan); - pinch.recognizeWith(rotate); - rotate.recognizeWith(pan); - - var doubleTap = new Hammer.Tap({ - event: 'doubletap', - taps: 2, - interval: 1500, - threshold: 50, - posThreshold: 50 - }); - - doubleTap.recognizeWith(pan); - - // add to the Manager - mc.add([ doubleTap, pan, rotate, pinch ]); - mc.on('tap doubletap panstart panmove panend pinchstart pinchmove rotatemove', onTouch); - - cornerstoneTools.preventGhostClick.enable(element); - $(element).on('touchstart touchend', onTouch); - $(element).data('hammer', mc); - //console.log('touchInput enabled'); - } - - function disable(element) { - cornerstoneTools.preventGhostClick.disable(element); - $(element).off('touchstart touchend', onTouch); - var mc = $(element).data('hammer'); - if (mc) { - mc.off('tap doubletap panstart panmove panend pinchmove rotatemove', onTouch); - } - - //console.log('touchInput disabled'); - } - - // module exports - cornerstoneTools.touchInput = { - enable: enable, - disable: disable - }; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/inputSources/touchInput.js - -// Begin Source: src/imageTools/simpleMouseButtonTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function simpleMouseButtonTool(mouseDownCallback) { - var configuration = {}; - - var toolInterface = { - activate: function(element, mouseButtonMask, options) { - $(element).off('CornerstoneToolsMouseDownActivate', mouseDownCallback); - var eventData = { - mouseButtonMask: mouseButtonMask, - options: options - }; - $(element).on('CornerstoneToolsMouseDownActivate', eventData, mouseDownCallback); - }, - disable: function(element) {$(element).off('CornerstoneToolsMouseDownActivate', mouseDownCallback);}, - enable: function(element) {$(element).off('CornerstoneToolsMouseDownActivate', mouseDownCallback);}, - deactivate: function(element) {$(element).off('CornerstoneToolsMouseDownActivate', mouseDownCallback);}, - getConfiguration: function() { return configuration;}, - setConfiguration: function(config) {configuration = config;} - }; - return toolInterface; - } - - // module exports - cornerstoneTools.simpleMouseButtonTool = simpleMouseButtonTool; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/simpleMouseButtonTool.js - -// Begin Source: src/imageTools/mouseButtonTool.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function mouseButtonTool(mouseToolInterface) { - var configuration = {}; - - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurement(mouseEventData) { - var element = mouseEventData.element; - - var measurementData = mouseToolInterface.createNewMeasurement(mouseEventData); - if (!measurementData) { - return; - } - - var eventData = { - mouseButtonMask: mouseEventData.which - }; - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(mouseEventData.element, mouseToolInterface.toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback); - - if (mouseToolInterface.mouseDoubleClickCallback) { - $(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback); - } - - cornerstone.updateImage(element); - - var handleMover; - if (Object.keys(measurementData.handles).length === 1) { - handleMover = cornerstoneTools.moveHandle; - } else { - handleMover = cornerstoneTools.moveNewHandle; - } - - var preventHandleOutsideImage; - if (mouseToolInterface.options && mouseToolInterface.options.preventHandleOutsideImage !== undefined) { - preventHandleOutsideImage = mouseToolInterface.options.preventHandleOutsideImage; - } else { - preventHandleOutsideImage = false; - } - - handleMover(mouseEventData, mouseToolInterface.toolType, measurementData, measurementData.handles.end, function() { - measurementData.active = false; - measurementData.invalidated = true; - if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, mouseToolInterface.toolType, measurementData); - } - - $(element).on('CornerstoneToolsMouseMove', eventData, mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, mouseToolInterface.mouseDownCallback || mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', eventData, mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback); - - if (mouseToolInterface.mouseDoubleClickCallback) { - $(element).on('CornerstoneToolsMouseDoubleClick', eventData, mouseToolInterface.mouseDoubleClickCallback); - } - - cornerstone.updateImage(element); - }, preventHandleOutsideImage); - } - - function mouseDownActivateCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - if (mouseToolInterface.addNewMeasurement) { - mouseToolInterface.addNewMeasurement(eventData); - } else { - addNewMeasurement(eventData); - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - ///////// END ACTIVE TOOL /////// - - ///////// BEGIN DEACTIVE TOOL /////// - - function mouseMoveCallback(e, eventData) { - cornerstoneTools.toolCoordinates.setCoords(eventData); - // if a mouse button is down, do nothing - if (eventData.which !== 0) { - return; - } - - // if we have no tool data for this element, do nothing - var toolData = cornerstoneTools.getToolState(eventData.element, mouseToolInterface.toolType); - if (!toolData) { - return; - } - - // We have tool data, search through all data - // and see if we can activate a handle - var imageNeedsUpdate = false; - for (var i = 0; i < toolData.data.length; i++) { - // get the cursor position in canvas coordinates - var coords = eventData.currentPoints.canvas; - - var data = toolData.data[i]; - if (cornerstoneTools.handleActivator(eventData.element, data.handles, coords) === true) { - imageNeedsUpdate = true; - } - - if ((mouseToolInterface.pointNearTool(eventData.element, data, coords) && !data.active) || (!mouseToolInterface.pointNearTool(eventData.element, data, coords) && data.active)) { - data.active = !data.active; - imageNeedsUpdate = true; - } - } - - // Handle activation status changed, redraw the image - if (imageNeedsUpdate === true) { - cornerstone.updateImage(eventData.element); - } - } - - function mouseDownCallback(e, eventData) { - var data; - var element = eventData.element; - - function handleDoneMove() { - data.invalidated = true; - if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, mouseToolInterface.toolType, data); - } - - cornerstone.updateImage(element); - $(element).on('CornerstoneToolsMouseMove', eventData, mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - } - - if (!cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - return; - } - - var coords = eventData.startPoints.canvas; - var toolData = cornerstoneTools.getToolState(e.currentTarget, mouseToolInterface.toolType); - if (!toolData) { - return; - } - - var i; - - // now check to see if there is a handle we can move - - var preventHandleOutsideImage; - if (mouseToolInterface.options && mouseToolInterface.options.preventHandleOutsideImage !== undefined) { - preventHandleOutsideImage = mouseToolInterface.options.preventHandleOutsideImage; - } else { - preventHandleOutsideImage = false; - } - - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - var distance = 6; - var handle = cornerstoneTools.getHandleNearImagePoint(element, data.handles, coords, distance); - if (handle) { - $(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - data.active = true; - cornerstoneTools.moveHandle(eventData, mouseToolInterface.toolType, data, handle, handleDoneMove, preventHandleOutsideImage); - e.stopImmediatePropagation(); - return false; - } - } - - // Now check to see if there is a line we can move - // now check to see if we have a tool that we can move - if (!mouseToolInterface.pointNearTool) { - return; - } - - var options = mouseToolInterface.options || { - deleteIfHandleOutsideImage: true, - preventHandleOutsideImage: false - }; - - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - data.active = false; - if (mouseToolInterface.pointNearTool(element, data, coords)) { - data.active = true; - $(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - cornerstoneTools.moveAllHandles(e, data, toolData, mouseToolInterface.toolType, options, handleDoneMove); - e.stopImmediatePropagation(); - return false; - } - } - } - ///////// END DEACTIVE TOOL /////// - - // not visible, not interactive - function disable(element) { - $(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback); - - if (mouseToolInterface.mouseDoubleClickCallback) { - $(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback); - } - - cornerstone.updateImage(element); - } - - // visible but not interactive - function enable(element) { - $(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback); - - if (mouseToolInterface.mouseDoubleClickCallback) { - $(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback); - } - - $(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - - cornerstone.updateImage(element); - } - - // visible, interactive and can create - function activate(element, mouseButtonMask) { - var eventData = { - mouseButtonMask: mouseButtonMask - }; - - $(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback); - - $(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).on('CornerstoneToolsMouseMove', eventData, mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, mouseToolInterface.mouseDownCallback || mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', eventData, mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback); - - if (mouseToolInterface.mouseDoubleClickCallback) { - $(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback); - $(element).on('CornerstoneToolsMouseDoubleClick', eventData, mouseToolInterface.mouseDoubleClickCallback); - } - - cornerstone.updateImage(element); - } - - // visible, interactive - function deactivate(element, mouseButtonMask) { - var eventData = { - mouseButtonMask: mouseButtonMask - }; - - var eventType = 'CornerstoneToolsToolDeactivated'; - var statusChangeEventData = { - mouseButtonMask: mouseButtonMask, - toolType: mouseToolInterface.toolType, - type: eventType - }; - - var event = $.Event(eventType, statusChangeEventData); - $(element).trigger(event, statusChangeEventData); - - $(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback); - - $(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).on('CornerstoneToolsMouseMove', eventData, mouseToolInterface.mouseMoveCallback || mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, mouseToolInterface.mouseDownCallback || mouseDownCallback); - - if (mouseToolInterface.mouseDoubleClickCallback) { - $(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback); - $(element).on('CornerstoneToolsMouseDoubleClick', eventData, mouseToolInterface.mouseDoubleClickCallback); - } - - cornerstone.updateImage(element); - } - - function getConfiguration() { - return configuration; - } - - function setConfiguration(config) { - configuration = config; - } - - var toolInterface = { - enable: enable, - disable: disable, - activate: activate, - deactivate: deactivate, - getConfiguration: getConfiguration, - setConfiguration: setConfiguration, - mouseDownCallback: mouseDownCallback, - mouseMoveCallback: mouseMoveCallback, - mouseDownActivateCallback: mouseDownActivateCallback - }; - - // Expose pointNearTool if available - if (mouseToolInterface.pointNearTool) { - toolInterface.pointNearTool = mouseToolInterface.pointNearTool; - } - - if (mouseToolInterface.mouseDoubleClickCallback) { - toolInterface.mouseDoubleClickCallback = mouseToolInterface.mouseDoubleClickCallback; - } - - if (mouseToolInterface.addNewMeasurement) { - toolInterface.addNewMeasurement = mouseToolInterface.addNewMeasurement; - } - - return toolInterface; - } - - // module exports - cornerstoneTools.mouseButtonTool = mouseButtonTool; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/mouseButtonTool.js - -// Begin Source: src/imageTools/mouseButtonRectangleTool.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function mouseButtonRectangleTool(mouseToolInterface, preventHandleOutsideImage) { - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurement(mouseEventData) { - var measurementData = mouseToolInterface.createNewMeasurement(mouseEventData); - - //prevent adding new measurement if tool returns nill - if (!measurementData) { - return; - } - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(mouseEventData.element, mouseToolInterface.toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(mouseEventData.element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - cornerstoneTools.moveHandle(mouseEventData, mouseToolInterface.toolType, measurementData, measurementData.handles.end, function() { - measurementData.active = false; - if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(mouseEventData.element, mouseToolInterface.toolType, measurementData); - } - - $(mouseEventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback); - }, preventHandleOutsideImage); - } - - function mouseDownActivateCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - addNewMeasurement(eventData); - return false; // false = cases jquery to preventDefault() and stopPropagation() this event - } - } - ///////// END ACTIVE TOOL /////// - - ///////// BEGIN DEACTIVE TOOL /////// - - function mouseMoveCallback(e, eventData) { - cornerstoneTools.toolCoordinates.setCoords(eventData); - // if a mouse button is down, do nothing - if (eventData.which !== 0) { - return; - } - - // if we have no tool data for this element, do nothing - var toolData = cornerstoneTools.getToolState(eventData.element, mouseToolInterface.toolType); - if (toolData === undefined) { - return; - } - - // We have tool data, search through all data - // and see if we can activate a handle - var imageNeedsUpdate = false; - var coords = eventData.currentPoints.canvas; - - for (var i = 0; i < toolData.data.length; i++) { - // get the cursor position in image coordinates - var data = toolData.data[i]; - if (cornerstoneTools.handleActivator(eventData.element, data.handles, coords) === true) { - imageNeedsUpdate = true; - } - - if ((mouseToolInterface.pointInsideRect(eventData.element, data, coords) && !data.active) || (!mouseToolInterface.pointInsideRect(eventData.element, data, coords) && data.active)) { - data.active = !data.active; - imageNeedsUpdate = true; - } - } - - // Handle activation status changed, redraw the image - if (imageNeedsUpdate === true) { - cornerstone.updateImage(eventData.element); - } - } - - function mouseDownCallback(e, eventData) { - var data; - - function handleDoneMove() { - data.active = false; - if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(eventData.element, mouseToolInterface.toolType, data); - } - - cornerstone.updateImage(eventData.element); - $(eventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback); - } - - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - var coords = eventData.startPoints.canvas; - var toolData = cornerstoneTools.getToolState(e.currentTarget, mouseToolInterface.toolType); - - var i; - - // now check to see if there is a handle we can move - var distanceSq = 25; - - if (toolData !== undefined) { - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - var handle = cornerstoneTools.getHandleNearImagePoint(eventData.element, data.handles, coords, distanceSq); - if (handle !== undefined) { - $(eventData.element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - data.active = true; - cornerstoneTools.moveHandle(eventData, mouseToolInterface.toolType, data, handle, handleDoneMove, preventHandleOutsideImage); - e.stopImmediatePropagation(); - return false; - } - } - } - - // Now check to see if there is a line we can move - // now check to see if we have a tool that we can move - var options = { - deleteIfHandleOutsideImage: true, - preventHandleOutsideImage: preventHandleOutsideImage - }; - - if (toolData !== undefined && mouseToolInterface.pointInsideRect !== undefined) { - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (mouseToolInterface.pointInsideRect(eventData.element, data, coords)) { - $(eventData.element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - cornerstoneTools.moveAllHandles(e, data, toolData, mouseToolInterface.toolType, options, handleDoneMove); - $(eventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback); - e.stopImmediatePropagation(); - return false; - } - } - } - } - } - ///////// END DEACTIVE TOOL /////// - - // not visible, not interactive - function disable(element) { - $(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseDownActivateCallback); - - cornerstone.updateImage(element); - } - - // visible but not interactive - function enable(element) { - $(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseDownActivateCallback); - - $(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - - cornerstone.updateImage(element); - } - - // visible, interactive and can create - function activate(element, mouseButtonMask) { - var eventData = { - mouseButtonMask: mouseButtonMask - }; - - $(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseDownActivateCallback); - - $(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).on('CornerstoneToolsMouseMove', eventData, mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', eventData, mouseDownActivateCallback); - - cornerstone.updateImage(element); - } - - // visible, interactive - function deactivate(element, mouseButtonMask) { - var eventData = { - mouseButtonMask: mouseButtonMask - }; - - $(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', mouseDownActivateCallback); - - $(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered); - $(element).on('CornerstoneToolsMouseMove', eventData, mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); - - cornerstone.updateImage(element); - } - - var toolInterface = { - enable: enable, - disable: disable, - activate: activate, - deactivate: deactivate - }; - - return toolInterface; - } - - // module exports - cornerstoneTools.mouseButtonRectangleTool = mouseButtonRectangleTool; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/mouseButtonRectangleTool.js - -// Begin Source: src/imageTools/mouseWheelTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function mouseWheelTool(mouseWheelCallback) { - var toolInterface = { - activate: function(element) { - $(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback); - var eventData = { - }; - $(element).on('CornerstoneToolsMouseWheel', eventData, mouseWheelCallback); - }, - disable: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);}, - enable: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);}, - deactivate: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);} - }; - return toolInterface; - } - - // module exports - cornerstoneTools.mouseWheelTool = mouseWheelTool; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/mouseWheelTool.js - -// Begin Source: src/imageTools/touchDragTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function touchDragTool(touchDragCallback, options) { - var events = 'CornerstoneToolsTouchDrag'; - if (options && options.fireOnTouchStart === true) { - events += ' CornerstoneToolsTouchStart'; - } - - var toolInterface = { - activate: function(element) { - $(element).off(events, touchDragCallback); - - if (options && options.eventData) { - $(element).on(events, options.eventData, touchDragCallback); - } else { - $(element).on(events, touchDragCallback); - } - - if (options && options.activateCallback) { - options.activateCallback(element); - } - }, - disable: function(element) { - $(element).off(events, touchDragCallback); - if (options && options.disableCallback) { - options.disableCallback(element); - } - }, - enable: function(element) { - $(element).off(events, touchDragCallback); - if (options && options.enableCallback) { - options.enableCallback(element); - } - }, - deactivate: function(element) { - $(element).off(events, touchDragCallback); - if (options && options.deactivateCallback) { - options.deactivateCallback(element); - } - } - }; - return toolInterface; - } - - // module exports - cornerstoneTools.touchDragTool = touchDragTool; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/touchDragTool.js - -// Begin Source: src/imageTools/touchPinchTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - /*jshint newcap: false */ - - function touchPinchTool(touchPinchCallback) { - var toolInterface = { - activate: function(element) { - $(element).off('CornerstoneToolsTouchPinch', touchPinchCallback); - var eventData = { - }; - $(element).on('CornerstoneToolsTouchPinch', eventData, touchPinchCallback); - }, - disable: function(element) {$(element).off('CornerstoneToolsTouchPinch', touchPinchCallback);}, - enable: function(element) {$(element).off('CornerstoneToolsTouchPinch', touchPinchCallback);}, - deactivate: function(element) {$(element).off('CornerstoneToolsTouchPinch', touchPinchCallback);} - }; - return toolInterface; - } - - // module exports - cornerstoneTools.touchPinchTool = touchPinchTool; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/touchPinchTool.js - -// Begin Source: src/imageTools/touchTool.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function deactivateAllHandles(handles) { - Object.keys(handles).forEach(function(name) { - var handle = handles[name]; - handle.active = false; - }); - } - - function deactivateAllToolInstances(toolData) { - if (!toolData) { - return; - } - - for (var i = 0; i < toolData.data.length; i++) { - var data = toolData.data[i]; - data.active = false; - if (!data.handles) { - continue; - } - - deactivateAllHandles(data.handles); - } - } - - function touchTool(touchToolInterface) { - ///////// BEGIN ACTIVE TOOL /////// - - function addNewMeasurement(touchEventData) { - //console.log('touchTool addNewMeasurement'); - var element = touchEventData.element; - - var measurementData = touchToolInterface.createNewMeasurement(touchEventData); - if (!measurementData) { - return; - } - - cornerstoneTools.addToolState(element, touchToolInterface.toolType, measurementData); - - if (Object.keys(measurementData.handles).length === 1 && touchEventData.type === 'CornerstoneToolsTap') { - measurementData.active = false; - measurementData.handles.end.active = false; - measurementData.handles.end.highlight = false; - measurementData.invalidated = true; - if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, touchToolInterface.toolType, measurementData); - } - - cornerstone.updateImage(element); - return; - } - - $(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - - cornerstone.updateImage(element); - cornerstoneTools.moveNewHandleTouch(touchEventData, touchToolInterface.toolType, measurementData, measurementData.handles.end, function() { - measurementData.active = false; - measurementData.invalidated = true; - if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, touchToolInterface.toolType, measurementData); - } - - $(element).on('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - $(element).on('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - cornerstone.updateImage(element); - }); - } - - function touchDownActivateCallback(e, eventData) { - //console.log('touchTool touchDownActivateCallback'); - if (touchToolInterface.addNewMeasurement) { - touchToolInterface.addNewMeasurement(eventData); - } else { - addNewMeasurement(eventData); - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - ///////// END ACTIVE TOOL /////// - - ///////// BEGIN INACTIVE TOOL /////// - function tapCallback(e, eventData) { - //console.log('touchTool tapCallback'); - var element = eventData.element; - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(e.currentTarget, touchToolInterface.toolType); - var data; - var i; - - // Deactivate everything - deactivateAllToolInstances(toolData); - - function doneMovingCallback() { - //console.log('touchTool tapCallback doneMovingCallback'); - deactivateAllToolInstances(toolData); - if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, touchToolInterface.toolType, data); - } - - cornerstone.updateImage(element); - $(element).on('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - } - - // now check to see if there is a handle we can move - if (toolData) { - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - var distanceSq = 25; // Should probably make this a settable property later - var handle = cornerstoneTools.getHandleNearImagePoint(element, data.handles, coords, distanceSq); - if (handle) { - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - data.active = true; - handle.active = true; - cornerstone.updateImage(element); - cornerstoneTools.touchMoveHandle(e, touchToolInterface.toolType, data, handle, doneMovingCallback); - e.stopImmediatePropagation(); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - } - - // Now check to see if we have a tool that we can move - if (toolData && touchToolInterface.pointNearTool) { - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (touchToolInterface.pointNearTool(element, data, coords)) { - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - data.active = true; - cornerstone.updateImage(element); - cornerstoneTools.touchMoveAllHandles(e, data, toolData, touchToolInterface.toolType, true, doneMovingCallback); - e.stopImmediatePropagation(); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - } - - // If there is nothing to move, add a new instance of the tool - // Need to check here to see if activation is allowed! - if (touchToolInterface.touchDownActivateCallback) { - touchToolInterface.touchDownActivateCallback(e, eventData); - } else { - touchDownActivateCallback(e, eventData); - } - - return false; - } - - function touchStartCallback(e, eventData) { - //console.log('touchTool touchStartCallback'); - var element = eventData.element; - var coords = eventData.startPoints.canvas; - var data; - var toolData = cornerstoneTools.getToolState(e.currentTarget, touchToolInterface.toolType); - var i; - - function doneMovingCallback(lastEvent, lastEventData) { - //console.log('touchTool touchStartCallback doneMovingCallback'); - data.active = false; - data.invalidated = true; - if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(eventData.element, touchToolInterface.toolType, data); - } - - cornerstone.updateImage(eventData.element); - $(element).on('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - - if (touchToolInterface.pressCallback) { - $(element).on('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); - } - - if (lastEvent && lastEvent.type === 'CornerstoneToolsTouchPress') { - var event = $.Event(lastEvent.type, lastEventData); - $(element).trigger(event, lastEventData); - } - } - - // now check to see if there is a handle we can move - - // Average pixel width of index finger is 45-57 pixels - // https://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/ - var distance = 28; - if (!toolData) { - return; - } - - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - - var handle = cornerstoneTools.getHandleNearImagePoint(eventData.element, data.handles, coords, distance); - if (handle) { - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - if (touchToolInterface.pressCallback) { - $(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); - } - - data.active = true; - cornerstoneTools.touchMoveHandle(e, touchToolInterface.toolType, data, handle, doneMovingCallback); - e.stopImmediatePropagation(); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - // Now check to see if we have a tool that we can move - if (!touchToolInterface.pointNearTool) { - return; - } - - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - - if (touchToolInterface.pointNearTool(eventData.element, data, coords)) { - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - if (touchToolInterface.pressCallback) { - $(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); - } - - cornerstoneTools.touchMoveAllHandles(e, data, toolData, touchToolInterface.toolType, true, doneMovingCallback); - e.stopImmediatePropagation(); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - } - ///////// END INACTIVE TOOL /////// - - // not visible, not interactive - function disable(element) { - $(element).off('CornerstoneImageRendered', touchToolInterface.onImageRendered); - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - - if (touchToolInterface.doubleTapCallback) { - $(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback); - } - - if (touchToolInterface.pressCallback) { - $(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); - } - - cornerstone.updateImage(element); - } - - // visible but not interactive - function enable(element) { - $(element).off('CornerstoneImageRendered', touchToolInterface.onImageRendered); - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - - $(element).on('CornerstoneImageRendered', touchToolInterface.onImageRendered); - - if (touchToolInterface.doubleTapCallback) { - $(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback); - } - - if (touchToolInterface.pressCallback) { - $(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); - } - - cornerstone.updateImage(element); - } - - // visible, interactive and can create - function activate(element) { - //console.log('activate touchTool'); - - $(element).off('CornerstoneImageRendered', touchToolInterface.onImageRendered); - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - - $(element).on('CornerstoneImageRendered', touchToolInterface.onImageRendered); - $(element).on('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).on('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - - if (touchToolInterface.doubleTapCallback) { - $(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback); - $(element).on('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback); - } - - if (touchToolInterface.pressCallback) { - $(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); - $(element).on('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); - } - - cornerstone.updateImage(element); - } - - // visible, interactive - function deactivate(element) { - var eventType = 'CornerstoneToolsToolDeactivated'; - var statusChangeEventData = { - toolType: touchToolInterface.toolType, - type: eventType - }; - - var event = $.Event(eventType, statusChangeEventData); - $(element).trigger(event, statusChangeEventData); - - $(element).off('CornerstoneImageRendered', touchToolInterface.onImageRendered); - $(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - $(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - - $(element).on('CornerstoneImageRendered', touchToolInterface.onImageRendered); - $(element).on('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback); - //$(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback); - - if (touchToolInterface.doubleTapCallback) { - $(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback); - } - - if (touchToolInterface.pressCallback) { - $(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); - } - - cornerstone.updateImage(element); - } - - var toolInterface = { - enable: enable, - disable: disable, - activate: activate, - deactivate: deactivate, - touchStartCallback: touchToolInterface.touchStartCallback || touchStartCallback, - touchDownActivateCallback: touchToolInterface.touchDownActivateCallback || touchDownActivateCallback, - tapCallback: touchToolInterface.tapCallback || tapCallback - }; - - // Expose pointNearTool if available - if (touchToolInterface.pointNearTool) { - toolInterface.pointNearTool = touchToolInterface.pointNearTool; - } - - if (touchToolInterface.doubleTapCallback) { - toolInterface.doubleTapCallback = touchToolInterface.doubleTapCallback; - } - - if (touchToolInterface.pressCallback) { - toolInterface.pressCallback = touchToolInterface.pressCallback; - } - - if (touchToolInterface.addNewMeasurement) { - toolInterface.addNewMeasurement = touchToolInterface.addNewMeasurement; - } - - return toolInterface; - } - - // module exports - cornerstoneTools.touchTool = touchTool; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/touchTool.js - -// Begin Source: src/imageTools/AngleTool.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'angle'; - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - // create the measurement data for this tool with the end handle activated - var angleData = { - visible: true, - active: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x - 20, - y: mouseEventData.currentPoints.image.y + 10, - highlight: true, - active: false - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true - }, - start2: { - x: mouseEventData.currentPoints.image.x - 20, - y: mouseEventData.currentPoints.image.y + 10, - highlight: true, - active: false - }, - end2: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y + 20, - highlight: true, - active: false - } - } - }; - - return angleData; - } - ///////// END ACTIVE TOOL /////// - - function pointNearTool(element, data, coords) { - var lineSegment = { - start: cornerstone.pixelToCanvas(element, data.handles.start), - end: cornerstone.pixelToCanvas(element, data.handles.end) - }; - - var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - if (distanceToPoint < 5) { - return true; - } - - lineSegment.start = cornerstone.pixelToCanvas(element, data.handles.start2); - lineSegment.end = cornerstone.pixelToCanvas(element, data.handles.end2); - - distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - return (distanceToPoint < 5); - } - - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (toolData === undefined) { - return; - } - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - //activation color - var color; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var font = cornerstoneTools.textStyle.getFont(); - var config = cornerstoneTools.angle.getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - - // configurable shadow - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - var data = toolData.data[i]; - - //differentiate the color of activation tool - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } - - // draw the line - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - - var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end); - - context.moveTo(handleStartCanvas.x, handleStartCanvas.y); - context.lineTo(handleEndCanvas.x, handleEndCanvas.y); - - handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start2); - handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end2); - - context.moveTo(handleStartCanvas.x, handleStartCanvas.y); - context.lineTo(handleEndCanvas.x, handleEndCanvas.y); - context.stroke(); - - // draw the handles - cornerstoneTools.drawHandles(context, eventData, data.handles); - - // Draw the text - context.fillStyle = color; - - // Need to work on correct angle to measure. This is a cobb angle and we need to determine - // where lines cross to measure angle. For now it will show smallest angle. - var dx1 = (Math.ceil(data.handles.start.x) - Math.ceil(data.handles.end.x)) * eventData.image.columnPixelSpacing; - var dy1 = (Math.ceil(data.handles.start.y) - Math.ceil(data.handles.end.y)) * eventData.image.rowPixelSpacing; - var dx2 = (Math.ceil(data.handles.start2.x) - Math.ceil(data.handles.end2.x)) * eventData.image.columnPixelSpacing; - var dy2 = (Math.ceil(data.handles.start2.y) - Math.ceil(data.handles.end2.y)) * eventData.image.rowPixelSpacing; - - var angle = Math.acos(Math.abs(((dx1 * dx2) + (dy1 * dy2)) / (Math.sqrt((dx1 * dx1) + (dy1 * dy1)) * Math.sqrt((dx2 * dx2) + (dy2 * dy2))))); - angle = angle * (180 / Math.PI); - - var rAngle = cornerstoneTools.roundToDecimal(angle, 2); - var str = '00B0'; // degrees symbol - var text = rAngle.toString() + String.fromCharCode(parseInt(str, 16)); - - var textX = (handleStartCanvas.x + handleEndCanvas.x) / 2; - var textY = (handleStartCanvas.y + handleEndCanvas.y) / 2; - - context.font = font; - cornerstoneTools.drawTextBox(context, text, textX, textY, color); - context.restore(); - } - } - ///////// END IMAGE RENDERING /////// - - // module exports - cornerstoneTools.angle = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - - cornerstoneTools.angleTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/AngleTool.js - -// Begin Source: src/imageTools/annotation.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'arrowAnnotate'; - - // Define a callback to get your text annotation - // This could be used, e.g. to open a modal - function getTextCallback(doneChangingTextCallback) { - doneChangingTextCallback(prompt('Enter your annotation:')); - } - - function changeTextCallback(data, eventData, doneChangingTextCallback) { - doneChangingTextCallback(prompt('Change your annotation:')); - } - - var configuration = { - getTextCallback: getTextCallback, - changeTextCallback: changeTextCallback, - drawHandles: false, - drawHandlesOnHover: true, - arrowFirst: true - }; - - /// --- Mouse Tool --- /// - - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurement(mouseEventData) { - var measurementData = createNewMeasurement(mouseEventData); - - var eventData = { - mouseButtonMask: mouseEventData.which, - }; - - function doneChangingTextCallback(text) { - if (text !== null) { - measurementData.text = text; - } else { - cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); - } - - measurementData.active = false; - cornerstone.updateImage(mouseEventData.element); - - $(mouseEventData.element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.arrowAnnotate.mouseMoveCallback); - $(mouseEventData.element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.arrowAnnotate.mouseDownCallback); - $(mouseEventData.element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.arrowAnnotate.mouseDownActivateCallback); - $(mouseEventData.element).on('CornerstoneToolsMouseDoubleClick', eventData, cornerstoneTools.arrowAnnotate.mouseDoubleClickCallback); - } - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(mouseEventData.element).off('CornerstoneToolsMouseMove', cornerstoneTools.arrowAnnotate.mouseMoveCallback); - $(mouseEventData.element).off('CornerstoneToolsMouseDown', cornerstoneTools.arrowAnnotate.mouseDownCallback); - $(mouseEventData.element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.arrowAnnotate.mouseDownActivateCallback); - $(mouseEventData.element).off('CornerstoneToolsMouseDoubleClick', cornerstoneTools.arrowAnnotate.mouseDoubleClickCallback); - - cornerstone.updateImage(mouseEventData.element); - cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { - if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); - } - - var config = cornerstoneTools.arrowAnnotate.getConfiguration(); - if (measurementData.text === undefined) { - config.getTextCallback(doneChangingTextCallback); - } - - cornerstone.updateImage(mouseEventData.element); - }); - } - - function createNewMeasurement(mouseEventData) { - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - textBox: { - active: false, - hasMoved: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - } - } - }; - - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - function pointNearTool(element, data, coords) { - var lineSegment = { - start: cornerstone.pixelToCanvas(element, data.handles.start), - end: cornerstone.pixelToCanvas(element, data.handles.end) - }; - - var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - return (distanceToPoint < 25); - } - - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - var enabledElement = eventData.enabledElement; - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var font = cornerstoneTools.textStyle.getFont(); - var config = cornerstoneTools.arrowAnnotate.getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - var data = toolData.data[i]; - - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } - - // Draw the arrow - var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end); - - //config.arrowFirst = false; - if (config.arrowFirst) { - cornerstoneTools.drawArrow(context, handleEndCanvas, handleStartCanvas, color, lineWidth); - } else { - cornerstoneTools.drawArrow(context, handleStartCanvas, handleEndCanvas, color, lineWidth); - } - - var handleOptions = { - drawHandlesIfActive: (config && config.drawHandlesOnHover) - }; - - if (config.drawHandles) { - cornerstoneTools.drawHandles(context, eventData, data.handles, color, handleOptions); - } - - // Draw the text - if (data.text && data.text !== '') { - context.font = font; - - // Calculate the text coordinates. - var textWidth = context.measureText(data.text).width + 10; - var textHeight = cornerstoneTools.textStyle.getFontSize() + 10; - - var distance = Math.max(textWidth, textHeight) / 2 + 5; - if (handleEndCanvas.x < handleStartCanvas.x) { - distance = -distance; - } - - var textCoords; - if (!data.handles.textBox.hasMoved) { - if (config.arrowFirst) { - textCoords = { - x: handleEndCanvas.x - textWidth / 2 + distance, - y: handleEndCanvas.y - textHeight / 2 - }; - } else { - // If the arrow is at the End position, the text should - // be placed near the Start position - textCoords = { - x: handleStartCanvas.x - textWidth / 2 - distance, - y: handleStartCanvas.y - textHeight / 2 - }; - } - - var transform = cornerstone.internal.getTransform(enabledElement); - transform.invert(); - - var coords = transform.transformPoint(textCoords.x, textCoords.y); - data.handles.textBox.x = coords.x; - data.handles.textBox.y = coords.y; - } - - textCoords = cornerstone.pixelToCanvas(eventData.element, data.handles.textBox); - - var boundingBox = cornerstoneTools.drawTextBox(context, data.text, textCoords.x, textCoords.y, color); - data.handles.textBox.boundingBox = boundingBox; - - if (data.handles.textBox.hasMoved) { - // Draw dashed link line between tool and text - var link = { - start: {}, - end: {} - }; - - var midpointCanvas = { - x: (handleStartCanvas.x + handleEndCanvas.x) / 2, - y: (handleStartCanvas.y + handleEndCanvas.y) / 2, - }; - - var points = [ handleStartCanvas, handleEndCanvas, midpointCanvas ]; - - link.end.x = textCoords.x; - link.end.y = textCoords.y; - - link.start = cornerstoneMath.point.findClosestPoint(points, link.end); - - var boundingBoxPoints = [ { - // Top middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top - }, { - // Left middle point of bounding box - x: boundingBox.left, - y: boundingBox.top + boundingBox.height / 2 - }, { - // Bottom middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top + boundingBox.height - }, { - // Right middle point of bounding box - x: boundingBox.left + boundingBox.width, - y: boundingBox.top + boundingBox.height / 2 - }, - ]; - - link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); - - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 2, 3 ]); - context.moveTo(link.start.x, link.start.y); - context.lineTo(link.end.x, link.end.y); - context.stroke(); - } - } - - context.restore(); - } - } - // ---- Touch tool ---- - - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurementTouch(touchEventData) { - var element = touchEventData.element; - var measurementData = createNewMeasurement(touchEventData); - - function doneChangingTextCallback(text) { - if (text !== null) { - measurementData.text = text; - } else { - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - measurementData.active = false; - cornerstone.updateImage(element); - - $(element).on('CornerstoneToolsTouchPress', cornerstoneTools.arrowAnnotateTouch.pressCallback); - $(element).on('CornerstoneToolsTouchStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback); - } - - cornerstoneTools.addToolState(element, toolType, measurementData); - $(element).off('CornerstoneToolsTouchPress', cornerstoneTools.arrowAnnotateTouch.pressCallback); - $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback); - cornerstone.updateImage(element); - - cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() { - cornerstone.updateImage(element); - - if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - var config = cornerstoneTools.arrowAnnotate.getConfiguration(); - if (measurementData.text === undefined) { - config.getTextCallback(doneChangingTextCallback); - } - }); - } - - function doubleClickCallback(e, eventData) { - var element = eventData.element; - var data; - - function doneChangingTextCallback(data, updatedText, deleteTool) { - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - } else { - data.text = updatedText; - } - - data.active = false; - cornerstone.updateImage(element); - } - - if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - return; - } - - var config = cornerstoneTools.arrowAnnotate.getConfiguration(); - - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); - - // now check to see if there is a handle we can move - if (!toolData) { - return; - } - - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords) || - cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { - data.active = true; - cornerstone.updateImage(element); - // Allow relabelling via a callback - config.changeTextCallback(data, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - } - } - - function pressCallback(e, eventData) { - var element = eventData.element; - var data; - - function doneChangingTextCallback(data, updatedText, deleteTool) { - console.log('pressCallback doneChangingTextCallback'); - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - } else { - data.text = updatedText; - } - - data.active = false; - cornerstone.updateImage(element); - - $(element).on('CornerstoneToolsTouchStart', cornerstoneTools.arrowAnnotateTouch.touchStartCallback); - $(element).on('CornerstoneToolsTouchStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback); - } - - if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - return; - } - - var config = cornerstoneTools.arrowAnnotate.getConfiguration(); - - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); - - // now check to see if there is a handle we can move - if (!toolData) { - return; - } - - if (eventData.handlePressed) { - $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.arrowAnnotateTouch.touchStartCallback); - $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback); - - // Allow relabelling via a callback - config.changeTextCallback(eventData.handlePressed, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords) || - cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { - data.active = true; - cornerstone.updateImage(element); - - $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.arrowAnnotateTouch.touchStartCallback); - $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback); - - // Allow relabelling via a callback - config.changeTextCallback(data, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - cornerstoneTools.arrowAnnotate = cornerstoneTools.mouseButtonTool({ - addNewMeasurement: addNewMeasurement, - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType, - mouseDoubleClickCallback: doubleClickCallback - }); - - cornerstoneTools.arrowAnnotate.setConfiguration(configuration); - - cornerstoneTools.arrowAnnotateTouch = cornerstoneTools.touchTool({ - addNewMeasurement: addNewMeasurementTouch, - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType, - pressCallback: pressCallback - }); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/annotation.js - -// Begin Source: src/imageTools/crosshairs.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var toolType = 'crosshairs'; - - function chooseLocation(e, eventData) { - e.stopImmediatePropagation(); // Prevent CornerstoneToolsTouchStartActive from killing any press events - - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - // Get current element target information - var sourceElement = e.currentTarget; - var sourceEnabledElement = cornerstone.getEnabledElement(sourceElement); - var sourceImageId = sourceEnabledElement.image.imageId; - var sourceImagePlane = cornerstoneTools.metaData.get('imagePlane', sourceImageId); - - // Get currentPoints from mouse cursor on selected element - var sourceImagePoint = eventData.currentPoints.image; - - // Transfer this to a patientPoint given imagePlane metadata - var patientPoint = cornerstoneTools.imagePointToPatientPoint(sourceImagePoint, sourceImagePlane); - - // Get the enabled elements associated with this synchronization context - var syncContext = toolData.data[0].synchronizationContext; - var enabledElements = syncContext.getSourceElements(); - - // Iterate over each synchronized element - $.each(enabledElements, function(index, targetElement) { - // don't do anything if the target is the same as the source - if (targetElement === sourceElement) { - return; // Same as 'continue' in a normal for loop - } - - var minDistance = Number.MAX_VALUE; - var newImageIdIndex = -1; - - var stackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack'); - if (stackToolDataSource === undefined) { - return; // Same as 'continue' in a normal for loop - } - - var stackData = stackToolDataSource.data[0]; - - // Find within the element's stack the closest image plane to selected location - $.each(stackData.imageIds, function(index, imageId) { - var imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId); - var imagePosition = imagePlane.imagePositionPatient; - var row = imagePlane.rowCosines.clone(); - var column = imagePlane.columnCosines.clone(); - var normal = column.clone().cross(row.clone()); - var distance = Math.abs(normal.clone().dot(imagePosition) - normal.clone().dot(patientPoint)); - //console.log(index + '=' + distance); - if (distance < minDistance) { - minDistance = distance; - newImageIdIndex = index; - } - }); - - if (newImageIdIndex === stackData.currentImageIdIndex) { - return; - } - - // Switch the loaded image to the required image - if (newImageIdIndex !== -1 && stackData.imageIds[newImageIdIndex] !== undefined) { - var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); - var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler(); - var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - - if (startLoadingHandler) { - startLoadingHandler(targetElement); - } - - var loader; - if (stackData.preventCache === true) { - loader = cornerstone.loadImage(stackData.imageIds[newImageIdIndex]); - } else { - loader = cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]); - } - - loader.then(function(image) { - var viewport = cornerstone.getViewport(targetElement); - stackData.currentImageIdIndex = newImageIdIndex; - cornerstone.displayImage(targetElement, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(targetElement, image); - } - }, function(error) { - var imageId = stackData.imageIds[newImageIdIndex]; - if (errorLoadingHandler) { - errorLoadingHandler(targetElement, imageId, error); - } - }); - } - }); - } - - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseDrag', mouseDragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - $(eventData.element).on('CornerstoneToolsMouseDrag', mouseDragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - chooseLocation(e, eventData); - return false; // false = cases jquery to preventDefault() and stopPropagation() this event - } - } - - function mouseDragCallback(e, eventData) { - chooseLocation(e, eventData); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - function enable(element, mouseButtonMask, synchronizationContext) { - var eventData = { - mouseButtonMask: mouseButtonMask, - }; - - // Clear any currently existing toolData - var toolData = cornerstoneTools.getToolState(element, toolType); - toolData = []; - - cornerstoneTools.addToolState(element, toolType, { - synchronizationContext: synchronizationContext, - }); - - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - - $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); - } - - // disables the reference line tool for the given element - function disable(element) { - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - } - - // module/private exports - cornerstoneTools.crosshairs = { - activate: enable, - deactivate: disable, - enable: enable, - disable: disable - }; - - function dragEndCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsTouchDrag', dragCallback); - $(eventData.element).off('CornerstoneToolsDragEnd', dragEndCallback); - } - - function dragStartCallback(e, eventData) { - $(eventData.element).on('CornerstoneToolsTouchDrag', dragCallback); - $(eventData.element).on('CornerstoneToolsDragEnd', dragEndCallback); - chooseLocation(e, eventData); - return false; - } - - function dragCallback(e, eventData) { - chooseLocation(e, eventData); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - function enableTouch(element, synchronizationContext) { - // Clear any currently existing toolData - var toolData = cornerstoneTools.getToolState(element, toolType); - toolData = []; - - cornerstoneTools.addToolState(element, toolType, { - synchronizationContext: synchronizationContext, - }); - - $(element).off('CornerstoneToolsTouchStart', dragStartCallback); - - $(element).on('CornerstoneToolsTouchStart', dragStartCallback); - } - - // disables the reference line tool for the given element - function disableTouch(element) { - $(element).off('CornerstoneToolsTouchStart', dragStartCallback); - } - - cornerstoneTools.crosshairsTouch = { - activate: enableTouch, - deactivate: disableTouch, - enable: enableTouch, - disable: disableTouch - }; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/crosshairs.js - -// Begin Source: src/imageTools/displayTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function displayTool(onImageRendered) { - var configuration = {}; - - var toolInterface = { - disable: function(element) {$(element).off('CornerstoneImageRendered', onImageRendered);}, - enable: function(element) { - $(element).off('CornerstoneImageRendered', onImageRendered); - $(element).on('CornerstoneImageRendered', onImageRendered); - cornerstone.updateImage(element); - }, - getConfiguration: function() { return configuration; }, - setConfiguration: function(config) {configuration = config;} - }; - - return toolInterface; - } - - // module exports - cornerstoneTools.displayTool = displayTool; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/displayTool.js - -// Begin Source: src/imageTools/doubleTapTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function doubleTapTool(doubleTapCallback) { - var toolInterface = { - activate: function(element) { - $(element).off('CornerstoneToolsDoubleTap', doubleTapCallback); - var eventData = {}; - $(element).on('CornerstoneToolsDoubleTap', eventData, doubleTapCallback); - }, - disable: function(element) {$(element).off('CornerstoneToolsDoubleTap', doubleTapCallback);}, - enable: function(element) {$(element).off('CornerstoneToolsDoubleTap', doubleTapCallback);}, - deactivate: function(element) {$(element).off('CornerstoneToolsDoubleTap', doubleTapCallback);} - }; - return toolInterface; - } - - // module exports - cornerstoneTools.doubleTapTool = doubleTapTool; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/doubleTapTool.js - -// Begin Source: src/imageTools/doubleTapZoom.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function fitToWindowStrategy(eventData) { - cornerstone.fitToWindow(eventData.element); - } - - function doubleTapCallback(e, eventData) { - cornerstoneTools.doubleTapZoom.strategy(eventData); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - cornerstoneTools.doubleTapZoom = cornerstoneTools.doubleTapTool(doubleTapCallback); - cornerstoneTools.doubleTapZoom.strategies = { - default: fitToWindowStrategy - }; - cornerstoneTools.doubleTapZoom.strategy = fitToWindowStrategy; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/doubleTapZoom.js - -// Begin Source: src/imageTools/dragProbe.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var dragEventData; - - function defaultStrategy(eventData) { - var enabledElement = cornerstone.getEnabledElement(eventData.element); - - var context = enabledElement.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color = cornerstoneTools.toolColors.getActiveColor(); - var font = cornerstoneTools.textStyle.getFont(); - var fontHeight = cornerstoneTools.textStyle.getFontSize(); - var config = cornerstoneTools.dragProbe.getConfiguration(); - - context.save(); - - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - var x = Math.round(eventData.currentPoints.image.x); - var y = Math.round(eventData.currentPoints.image.y); - - var storedPixels; - var text, - str; - - if (x < 0 || y < 0 || x >= eventData.image.columns || y >= eventData.image.rows) { - return; - } - - if (eventData.image.color) { - storedPixels = cornerstoneTools.getRGBPixels(eventData.element, x, y, 1, 1); - text = '' + x + ', ' + y; - str = 'R: ' + storedPixels[0] + ' G: ' + storedPixels[1] + ' B: ' + storedPixels[2] + ' A: ' + storedPixels[3]; - } else { - storedPixels = cornerstone.getStoredPixels(eventData.element, x, y, 1, 1); - var sp = storedPixels[0]; - var mo = sp * eventData.image.slope + eventData.image.intercept; - var suv = cornerstoneTools.calculateSUV(eventData.image, sp); - - // Draw text - text = '' + x + ', ' + y; - str = 'SP: ' + sp + ' MO: ' + parseFloat(mo.toFixed(3)); - if (suv) { - str += ' SUV: ' + parseFloat(suv.toFixed(3)); - } - } - - // Draw text - var coords = { - // translate the x/y away from the cursor - x: eventData.currentPoints.image.x + 3, - y: eventData.currentPoints.image.y - 3 - }; - var textCoords = cornerstone.pixelToCanvas(eventData.element, coords); - - context.font = font; - context.fillStyle = color; - - cornerstoneTools.drawTextBox(context, str, textCoords.x, textCoords.y + fontHeight + 5, color); - cornerstoneTools.drawTextBox(context, text, textCoords.x, textCoords.y, color); - context.restore(); - } - - function minimalStrategy(eventData) { - var element = eventData.element; - var enabledElement = cornerstone.getEnabledElement(element); - var image = enabledElement.image; - - var context = enabledElement.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color = cornerstoneTools.toolColors.getActiveColor(); - var font = cornerstoneTools.textStyle.getFont(); - var config = cornerstoneTools.dragProbe.getConfiguration(); - - context.save(); - - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - var seriesModule = cornerstone.metaData.get('generalSeriesModule', image.imageId); - var modality; - if (seriesModule) { - modality = seriesModule.modality; - } - - var toolCoords; - if (eventData.isTouchEvent === true) { - toolCoords = cornerstone.pageToPixel(element, eventData.currentPoints.page.x, - eventData.currentPoints.page.y - cornerstoneTools.textStyle.getFontSize() * 4); - } else { - toolCoords = cornerstone.pageToPixel(element, eventData.currentPoints.page.x, - eventData.currentPoints.page.y - cornerstoneTools.textStyle.getFontSize() / 2); - } - - var storedPixels; - var text = ''; - - if (toolCoords.x < 0 || toolCoords.y < 0 || - toolCoords.x >= image.columns || toolCoords.y >= image.rows) { - return; - } - - if (image.color) { - storedPixels = cornerstoneTools.getRGBPixels(element, toolCoords.x, toolCoords.y, 1, 1); - text = 'R: ' + storedPixels[0] + ' G: ' + storedPixels[1] + ' B: ' + storedPixels[2]; - } else { - storedPixels = cornerstone.getStoredPixels(element, toolCoords.x, toolCoords.y, 1, 1); - var sp = storedPixels[0]; - var mo = sp * eventData.image.slope + eventData.image.intercept; - - var modalityPixelValueText = parseFloat(mo.toFixed(2)); - if (modality === 'CT') { - text += 'HU: ' + modalityPixelValueText; - } else if (modality === 'PT') { - text += modalityPixelValueText; - var suv = cornerstoneTools.calculateSUV(eventData.image, sp); - if (suv) { - text += ' SUV: ' + parseFloat(suv.toFixed(2)); - } - } else { - text += modalityPixelValueText; - } - } - - // Prepare text - var textCoords = cornerstone.pixelToCanvas(element, toolCoords); - context.font = font; - context.fillStyle = color; - - // Translate the x/y away from the cursor - var translation; - var handleRadius = 6; - var width = context.measureText(text).width; - - if (eventData.isTouchEvent === true) { - translation = { - x: -width / 2 - 5, - y: -cornerstoneTools.textStyle.getFontSize() - 10 - 2 * handleRadius - }; - } else { - translation = { - x: 12, - y: -(cornerstoneTools.textStyle.getFontSize() + 10) / 2 - }; - } - - context.beginPath(); - context.strokeStyle = color; - context.arc(textCoords.x, textCoords.y, handleRadius, 0, 2 * Math.PI); - context.stroke(); - - cornerstoneTools.drawTextBox(context, text, textCoords.x + translation.x, textCoords.y + translation.y, color); - context.restore(); - } - - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneImageRendered', imageRenderedCallback); - $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); - cornerstone.updateImage(eventData.element); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - $(eventData.element).on('CornerstoneImageRendered', imageRenderedCallback); - $(eventData.element).on('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).on('CornerstoneToolsMouseClick', mouseUpCallback); - cornerstoneTools.dragProbe.strategy(eventData); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - function imageRenderedCallback() { - if (dragEventData) { - cornerstoneTools.dragProbe.strategy(dragEventData); - dragEventData = null; - } - } - - // The strategy can't be execute at this momento because the image is rendered asynchronously - // (requestAnimationFrame). Then the eventData that contains all information needed is being - // cached and the strategy will be executed once CornerstoneImageRendered is triggered. - function dragCallback(e, eventData) { - var element = eventData.element; - - dragEventData = eventData; - cornerstone.updateImage(element); - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - cornerstoneTools.dragProbe = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); - - cornerstoneTools.dragProbe.strategies = { - default: defaultStrategy, - minimal: minimalStrategy - }; - cornerstoneTools.dragProbe.strategy = defaultStrategy; - - var options = { - fireOnTouchStart: true - }; - cornerstoneTools.dragProbeTouch = cornerstoneTools.touchDragTool(dragCallback, options); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/dragProbe.js - -// Begin Source: src/imageTools/ellipticalRoi.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'ellipticalRoi'; - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - invalidated: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true - }, - textBox: { - active: false, - hasMoved: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - } - } - }; - - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - ///////// BEGIN IMAGE RENDERING /////// - function pointNearEllipse(element, data, coords, distance) { - var startCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - - var minorEllipse = { - left: Math.min(startCanvas.x, endCanvas.x) + distance / 2, - top: Math.min(startCanvas.y, endCanvas.y) + distance / 2, - width: Math.abs(startCanvas.x - endCanvas.x) - distance, - height: Math.abs(startCanvas.y - endCanvas.y) - distance - }; - - var majorEllipse = { - left: Math.min(startCanvas.x, endCanvas.x) - distance / 2, - top: Math.min(startCanvas.y, endCanvas.y) - distance / 2, - width: Math.abs(startCanvas.x - endCanvas.x) + distance, - height: Math.abs(startCanvas.y - endCanvas.y) + distance - }; - - var pointInMinorEllipse = cornerstoneTools.pointInEllipse(minorEllipse, coords); - var pointInMajorEllipse = cornerstoneTools.pointInEllipse(majorEllipse, coords); - - if (pointInMajorEllipse && !pointInMinorEllipse) { - return true; - } - - return false; - } - - function pointNearTool(element, data, coords) { - return pointNearEllipse(element, data, coords, 15); - } - - function pointNearToolTouch(element, data, coords) { - return pointNearEllipse(element, data, coords, 25); - } - - function numberWithCommas(x) { - // http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript - var parts = x.toString().split('.'); - parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); - return parts.join('.'); - } - - function onImageRendered(e, eventData) { - // If we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - var image = eventData.image; - var element = eventData.element; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var config = cornerstoneTools.ellipticalRoi.getConfiguration(); - var context = eventData.canvasContext.canvas.getContext('2d'); - var seriesModule = cornerstone.metaData.get('generalSeriesModule', image.imageId); - var modality; - if (seriesModule) { - modality = seriesModule.modality; - } - - context.setTransform(1, 0, 0, 1, 0, 0); - - // If we have tool data for this element - iterate over each set and draw it - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - - var data = toolData.data[i]; - - // Apply any shadow settings defined in the tool configuration - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - // Check which color the rendered tool should be - var color = cornerstoneTools.toolColors.getColorIfActive(data.active); - - // Convert Image coordinates to Canvas coordinates given the element - var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - - // Retrieve the bounds of the ellipse (left, top, width, and height) - // in Canvas coordinates - var leftCanvas = Math.min(handleStartCanvas.x, handleEndCanvas.x); - var topCanvas = Math.min(handleStartCanvas.y, handleEndCanvas.y); - var widthCanvas = Math.abs(handleStartCanvas.x - handleEndCanvas.x); - var heightCanvas = Math.abs(handleStartCanvas.y - handleEndCanvas.y); - - // Draw the ellipse on the canvas - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - cornerstoneTools.drawEllipse(context, leftCanvas, topCanvas, widthCanvas, heightCanvas); - context.closePath(); - - // If the tool configuration specifies to only draw the handles on hover / active, - // follow this logic - if (config && config.drawHandlesOnHover) { - // Draw the handles if the tool is active - if (data.active === true) { - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - } else { - // If the tool is inactive, draw the handles only if each specific handle is being - // hovered over - var handleOptions = { - drawHandlesIfActive: true - }; - cornerstoneTools.drawHandles(context, eventData, data.handles, color, handleOptions); - } - } else { - // If the tool has no configuration settings, always draw the handles - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - } - - // Define variables for the area and mean/standard deviation - var area, - meanStdDev, - meanStdDevSUV; - - // Perform a check to see if the tool has been invalidated. This is to prevent - // unnecessary re-calculation of the area, mean, and standard deviation if the - // image is re-rendered but the tool has not moved (e.g. during a zoom) - if (!data.invalidated) { - // If the data is not invalidated, retrieve it from the toolData - meanStdDev = data.meanStdDev; - meanStdDevSUV = data.meanStdDevSUV; - area = data.area; - } else { - // If the data has been invalidated, we need to calculate it again - - // Retrieve the bounds of the ellipse in image coordinates - var ellipse = { - left: Math.round(Math.min(data.handles.start.x, data.handles.end.x)), - top: Math.round(Math.min(data.handles.start.y, data.handles.end.y)), - width: Math.round(Math.abs(data.handles.start.x - data.handles.end.x)), - height: Math.round(Math.abs(data.handles.start.y - data.handles.end.y)) - }; - - // First, make sure this is not a color image, since no mean / standard - // deviation will be calculated for color images. - if (!image.color) { - // Retrieve the array of pixels that the ellipse bounds cover - var pixels = cornerstone.getPixels(element, ellipse.left, ellipse.top, ellipse.width, ellipse.height); - - // Calculate the mean & standard deviation from the pixels and the ellipse details - meanStdDev = cornerstoneTools.calculateEllipseStatistics(pixels, ellipse); - - if (modality === 'PT') { - // If the image is from a PET scan, use the DICOM tags to - // calculate the SUV from the mean and standard deviation. - - // Note that because we are using modality pixel values from getPixels, and - // the calculateSUV routine also rescales to modality pixel values, we are first - // returning the values to storedPixel values before calcuating SUV with them. - // TODO: Clean this up? Should we add an option to not scale in calculateSUV? - meanStdDevSUV = { - mean: cornerstoneTools.calculateSUV(image, (meanStdDev.mean - image.intercept) / image.slope), - stdDev: cornerstoneTools.calculateSUV(image, (meanStdDev.stdDev - image.intercept) / image.slope) - }; - } - - // If the mean and standard deviation values are sane, store them for later retrieval - if (meanStdDev && !isNaN(meanStdDev.mean)) { - data.meanStdDev = meanStdDev; - data.meanStdDevSUV = meanStdDevSUV; - } - } - - // Retrieve the pixel spacing values, and if they are not - // real non-zero values, set them to 1 - var columnPixelSpacing = image.columnPixelSpacing || 1; - var rowPixelSpacing = image.rowPixelSpacing || 1; - - // Calculate the image area from the ellipse dimensions and pixel spacing - area = Math.PI * (ellipse.width * columnPixelSpacing / 2) * (ellipse.height * rowPixelSpacing / 2); - - // If the area value is sane, store it for later retrieval - if (!isNaN(area)) { - data.area = area; - } - - // Set the invalidated flag to false so that this data won't automatically be recalculated - data.invalidated = false; - } - - // Define an array to store the rows of text for the textbox - var textLines = []; - - // If the mean and standard deviation values are present, display them - if (meanStdDev && meanStdDev.mean !== undefined) { - // If the modality is CT, add HU to denote Hounsfield Units - var moSuffix = ''; - if (modality === 'CT') { - moSuffix = ' HU'; - } - - // Create a line of text to display the mean and any units that were specified (i.e. HU) - var meanText = 'Mean: ' + numberWithCommas(meanStdDev.mean.toFixed(2)) + moSuffix; - // Create a line of text to display the standard deviation and any units that were specified (i.e. HU) - var stdDevText = 'StdDev: ' + numberWithCommas(meanStdDev.stdDev.toFixed(2)) + moSuffix; - - // If this image has SUV values to display, concatenate them to the text line - if (meanStdDevSUV && meanStdDevSUV.mean !== undefined) { - var SUVtext = ' SUV: '; - meanText += SUVtext + numberWithCommas(meanStdDevSUV.mean.toFixed(2)); - stdDevText += SUVtext + numberWithCommas(meanStdDevSUV.stdDev.toFixed(2)); - } - - // Add these text lines to the array to be displayed in the textbox - textLines.push(meanText); - textLines.push(stdDevText); - } - - // If the area is a sane value, display it - if (area) { - // Determine the area suffix based on the pixel spacing in the image. - // If pixel spacing is present, use millimeters. Otherwise, use pixels. - // This uses Char code 178 for a superscript 2 - var suffix = ' mm' + String.fromCharCode(178); - if (!image.rowPixelSpacing || !image.columnPixelSpacing) { - suffix = ' pixels' + String.fromCharCode(178); - } - - // Create a line of text to display the area and its units - var areaText = 'Area: ' + numberWithCommas(area.toFixed(2)) + suffix; - - // Add this text line to the array to be displayed in the textbox - textLines.push(areaText); - } - - // If the textbox has not been moved by the user, it should be displayed on the right-most - // side of the tool. - if (!data.handles.textBox.hasMoved) { - // Find the rightmost side of the ellipse at its vertical center, and place the textbox here - // Note that this calculates it in image coordinates - data.handles.textBox.x = Math.max(data.handles.start.x, data.handles.end.x); - data.handles.textBox.y = (data.handles.start.y + data.handles.end.y) / 2; - } - - // Convert the textbox Image coordinates into Canvas coordinates - var textCoords = cornerstone.pixelToCanvas(element, data.handles.textBox); - - // Set options for the textbox drawing function - var options = { - centering: { - x: false, - y: true - } - }; - - // Draw the textbox and retrieves it's bounding box for mouse-dragging and highlighting - var boundingBox = cornerstoneTools.drawTextBox(context, textLines, textCoords.x, - textCoords.y, color, options); - - // Store the bounding box data in the handle for mouse-dragging and highlighting - data.handles.textBox.boundingBox = boundingBox; - - // If the textbox has moved, we would like to draw a line linking it with the tool - // This section decides where to draw this line to on the Ellipse based on the location - // of the textbox relative to the ellipse. - if (data.handles.textBox.hasMoved) { - // Draw dashed link line between tool and text - - // The initial link position is at the center of the - // textbox. - var link = { - start: {}, - end: { - x: textCoords.x, - y: textCoords.y - } - }; - - // First we calculate the ellipse points (top, left, right, and bottom) - var ellipsePoints = [ { - // Top middle point of ellipse - x: leftCanvas + widthCanvas / 2, - y: topCanvas - }, { - // Left middle point of ellipse - x: leftCanvas, - y: topCanvas + heightCanvas / 2 - }, { - // Bottom middle point of ellipse - x: leftCanvas + widthCanvas / 2, - y: topCanvas + heightCanvas - }, { - // Right middle point of ellipse - x: leftCanvas + widthCanvas, - y: topCanvas + heightCanvas / 2 - } ]; - - // We obtain the link starting point by finding the closest point on the ellipse to the - // center of the textbox - link.start = cornerstoneMath.point.findClosestPoint(ellipsePoints, link.end); - - // Next we calculate the corners of the textbox bounding box - var boundingBoxPoints = [ { - // Top middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top - }, { - // Left middle point of bounding box - x: boundingBox.left, - y: boundingBox.top + boundingBox.height / 2 - }, { - // Bottom middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top + boundingBox.height - }, { - // Right middle point of bounding box - x: boundingBox.left + boundingBox.width, - y: boundingBox.top + boundingBox.height / 2 - }, ]; - - // Now we recalculate the link endpoint by identifying which corner of the bounding box - // is closest to the start point we just calculated. - link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); - - // Finally we draw the dashed linking line - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 2, 3 ]); - context.moveTo(link.start.x, link.start.y); - context.lineTo(link.end.x, link.end.y); - context.stroke(); - } - - context.restore(); - } - } - ///////// END IMAGE RENDERING /////// - - // module exports - cornerstoneTools.ellipticalRoi = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - cornerstoneTools.ellipticalRoiTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearToolTouch, - toolType: toolType - }); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/ellipticalRoi.js - -// Begin Source: src/imageTools/freehand.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'freehand'; - var configuration = { - mouseLocation: { - handles: { - start: { - highlight: true, - active: true, - } - } - }, - freehand: false, - modifying: false, - currentHandle: 0, - currentTool: -1 - }; - - ///////// BEGIN ACTIVE TOOL /////// - function addPoint(eventData) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (toolData === undefined) { - return; - } - - var config = cornerstoneTools.freehand.getConfiguration(); - - // Get the toolData from the last-drawn drawing - // (this should change when modification is added) - var data = toolData.data[config.currentTool]; - - var handleData = { - x: eventData.currentPoints.image.x, - y: eventData.currentPoints.image.y, - highlight: true, - active: true, - lines: [] - }; - - // If this is not the first handle - if (data.handles.length){ - // Add the line from the current handle to the new handle - data.handles[config.currentHandle - 1].lines.push(eventData.currentPoints.image); - } - - // Add the new handle - data.handles.push(handleData); - - // Increment the current handle value - config.currentHandle += 1; - - // Reset freehand value - config.freehand = false; - - // Force onImageRendered to fire - cornerstone.updateImage(eventData.element); - } - - function pointNearHandle(eventData, toolIndex) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (toolData === undefined) { - return; - } - - var data = toolData.data[toolIndex]; - if (data.handles === undefined) { - return; - } - - var mousePoint = eventData.currentPoints.canvas; - for (var i = 0; i < data.handles.length; i++) { - var handleCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles[i]); - if (cornerstoneMath.point.distance(handleCanvas, mousePoint) < 5) { - return i; - } - } - - return; - } - - function pointNearHandleAllTools(eventData) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData) { - return; - } - - var handleNearby; - for (var toolIndex = 0; toolIndex < toolData.data.length; toolIndex++) { - handleNearby = pointNearHandle(eventData, toolIndex); - if (handleNearby !== undefined) { - return { - handleNearby: handleNearby, - toolIndex: toolIndex - }; - } - } - } - - // --- Drawing loop --- - // On first click, add point - // After first click, on mouse move, record location - // If mouse comes close to previous point, snap to it - // On next click, add another point -- continuously - // On each click, if it intersects with a current point, end drawing loop - - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - - // Check if drawing is finished - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (toolData === undefined) { - return; - } - - var config = cornerstoneTools.freehand.getConfiguration(); - - if (!eventData.event.shiftKey) { - config.freehand = false; - } - - cornerstone.updateImage(eventData.element); - } - - function mouseMoveCallback(e, eventData) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData) { - return; - } - - var config = cornerstoneTools.freehand.getConfiguration(); - - var data = toolData.data[config.currentTool]; - - // Set the mouseLocation handle - var x = Math.max(eventData.currentPoints.image.x, 0); - x = Math.min(x, eventData.image.width); - config.mouseLocation.handles.start.x = x; - - var y = Math.max(eventData.currentPoints.image.y, 0); - y = Math.min(y, eventData.image.height); - config.mouseLocation.handles.start.y = y; - - var currentHandle = config.currentHandle; - - if (config.modifying) { - // Move the handle - data.active = true; - data.highlight = true; - data.handles[currentHandle].x = config.mouseLocation.handles.start.x; - data.handles[currentHandle].y = config.mouseLocation.handles.start.y; - if (currentHandle) { - var lastLineIndex = data.handles[currentHandle - 1].lines.length - 1; - var lastLine = data.handles[currentHandle - 1].lines[lastLineIndex]; - lastLine.x = config.mouseLocation.handles.start.x; - lastLine.y = config.mouseLocation.handles.start.y; - } - } - - if (config.freehand) { - data.handles[currentHandle - 1].lines.push(eventData.currentPoints.image); - } else { - // No snapping in freehand mode - var handleNearby = pointNearHandle(eventData, config.currentTool); - - // If there is a handle nearby to snap to - // (and it's not the actual mouse handle) - if (handleNearby !== undefined && handleNearby < (data.handles.length - 1)) { - config.mouseLocation.handles.start.x = data.handles[handleNearby].x; - config.mouseLocation.handles.start.y = data.handles[handleNearby].y; - } - } - - // Force onImageRendered - cornerstone.updateImage(eventData.element); - } - - function startDrawing(eventData) { - $(eventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - - var measurementData = { - visible: true, - active: true, - handles: [] - }; - - var config = cornerstoneTools.freehand.getConfiguration(); - config.mouseLocation.handles.start.x = eventData.currentPoints.image.x; - config.mouseLocation.handles.start.y = eventData.currentPoints.image.y; - - cornerstoneTools.addToolState(eventData.element, toolType, measurementData); - - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - config.currentTool = toolData.data.length - 1; - } - - function endDrawing(eventData, handleNearby) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData) { - return; - } - - var config = cornerstoneTools.freehand.getConfiguration(); - - var data = toolData.data[config.currentTool]; - - data.active = false; - data.highlight = false; - - // Connect the end of the drawing to the handle nearest to the click - if (handleNearby !== undefined){ - // only save x,y params from nearby handle to prevent circular reference - data.handles[config.currentHandle - 1].lines.push({ - x: data.handles[handleNearby].x, - y: data.handles[handleNearby].y - }); - } - - if (config.modifying) { - config.modifying = false; - } - - // Reset the current handle - config.currentHandle = 0; - config.currentTool = -1; - - $(eventData.element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - - cornerstone.updateImage(eventData.element); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - - var handleNearby, toolIndex; - - var config = cornerstoneTools.freehand.getConfiguration(); - var currentTool = config.currentTool; - - if (config.modifying) { - endDrawing(eventData); - return; - } - - if (currentTool < 0) { - var nearby = pointNearHandleAllTools(eventData); - if (nearby) { - handleNearby = nearby.handleNearby; - toolIndex = nearby.toolIndex; - // This means the user is trying to modify a point - if (handleNearby !== undefined) { - $(eventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - config.modifying = true; - config.currentHandle = handleNearby; - config.currentTool = toolIndex; - } - } else { - startDrawing(eventData); - addPoint(eventData); - } - } else if (currentTool >= 0 && toolData.data[currentTool].active) { - handleNearby = pointNearHandle(eventData, currentTool); - if (handleNearby !== undefined) { - endDrawing(eventData, handleNearby); - } else if (eventData.event.shiftKey) { - config.freehand = true; - } else { - addPoint(eventData); - } - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - ///////// END ACTIVE TOOL /////// - - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (toolData === undefined) { - return; - } - - var config = cornerstoneTools.freehand.getConfiguration(); - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var fillColor = cornerstoneTools.toolColors.getFillColor(); - - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - - var data = toolData.data[i]; - - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - fillColor = cornerstoneTools.toolColors.getFillColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - fillColor = cornerstoneTools.toolColors.getToolColor(); - } - - var handleStart; - - if (data.handles.length) { - for (var j = 0; j < data.handles.length; j++) { - // Draw a line between handle j and j+1 - handleStart = data.handles[j]; - var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, handleStart); - - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.moveTo(handleStartCanvas.x, handleStartCanvas.y); - - for (var k = 0; k < data.handles[j].lines.length; k++) { - var lineCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles[j].lines[k]); - context.lineTo(lineCanvas.x, lineCanvas.y); - context.stroke(); - } - - var mouseLocationCanvas = cornerstone.pixelToCanvas(eventData.element, config.mouseLocation.handles.start); - if (j === (data.handles.length - 1)) { - if (data.active && !config.freehand && !config.modifying) { - // If it's still being actively drawn, keep the last line to - // the mouse location - context.lineTo(mouseLocationCanvas.x, mouseLocationCanvas.y); - context.stroke(); - } - } - } - } - - // If the tool is active, draw a handle at the cursor location - var options = { - fill: fillColor - }; - - if (data.active){ - cornerstoneTools.drawHandles(context, eventData, config.mouseLocation.handles, color, options); - } - // draw the handles - cornerstoneTools.drawHandles(context, eventData, data.handles, color, options); - - context.restore(); - } - } - ///////// END IMAGE RENDERING /////// - function enable(element) { - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - $(element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneImageRendered', onImageRendered); - - $(element).on('CornerstoneImageRendered', onImageRendered); - cornerstone.updateImage(element); - } - - // disables the reference line tool for the given element - function disable(element) { - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - $(element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneImageRendered', onImageRendered); - cornerstone.updateImage(element); - } - - // visible and interactive - function activate(element, mouseButtonMask) { - var eventData = { - mouseButtonMask: mouseButtonMask, - }; - - $(element).off('CornerstoneToolsMouseDown', eventData, mouseDownCallback); - $(element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneImageRendered', onImageRendered); - - $(element).on('CornerstoneImageRendered', onImageRendered); - $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); - - cornerstone.updateImage(element); - } - - // visible, but not interactive - function deactivate(element) { - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - $(element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneImageRendered', onImageRendered); - - $(element).on('CornerstoneImageRendered', onImageRendered); - - cornerstone.updateImage(element); - } - - function getConfiguration() { - return configuration; - } - - function setConfiguration(config) { - configuration = config; - } - - // module/private exports - cornerstoneTools.freehand = { - enable: enable, - disable: disable, - activate: activate, - deactivate: deactivate, - getConfiguration: getConfiguration, - setConfiguration: setConfiguration - }; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/freehand.js - -// Begin Source: src/imageTools/highlight.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'highlight'; - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - //if already a highlight measurement, creating a new one will be useless - var existingToolData = cornerstoneTools.getToolState(mouseEventData.event.currentTarget, toolType); - if (existingToolData && existingToolData.data && existingToolData.data.length > 0) { - return; - } - - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true - } - } - }; - - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - function pointInsideRect(element, data, coords) { - var startCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - - var rect = { - left: Math.min(startCanvas.x, endCanvas.x), - top: Math.min(startCanvas.y, endCanvas.y), - width: Math.abs(startCanvas.x - endCanvas.x), - height: Math.abs(startCanvas.y - endCanvas.y) - }; - - var insideBox = false; - if ((coords.x >= rect.left && coords.x <= (rect.left + rect.width)) && coords.y >= rect.top && coords.y <= (rect.top + rect.height)) { - insideBox = true; - } - - return insideBox; - } - - function pointNearTool(element, data, coords) { - var startCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - - var rect = { - left: Math.min(startCanvas.x, endCanvas.x), - top: Math.min(startCanvas.y, endCanvas.y), - width: Math.abs(startCanvas.x - endCanvas.x), - height: Math.abs(startCanvas.y - endCanvas.y) - }; - - var distanceToPoint = cornerstoneMath.rect.distanceToPoint(rect, coords); - return (distanceToPoint < 5); - } - - ///////// BEGIN IMAGE RENDERING /////// - - function onImageRendered(e, eventData) { - - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (toolData === undefined) { - return; - } - - // we have tool data for this elemen - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - - context.save(); - - var data = toolData.data[0]; - - if (!data) { - return; - } - - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } - - var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end); - - var rect = { - left: Math.min(handleStartCanvas.x, handleEndCanvas.x), - top: Math.min(handleStartCanvas.y, handleEndCanvas.y), - width: Math.abs(handleStartCanvas.x - handleEndCanvas.x), - height: Math.abs(handleStartCanvas.y - handleEndCanvas.y) - }; - - // draw dark fill outside the rectangle - context.beginPath(); - context.strokeStyle = 'transparent'; - - context.rect(0, 0, context.canvas.clientWidth, context.canvas.clientHeight); - - context.rect(rect.width + rect.left, rect.top, -rect.width, rect.height); - context.stroke(); - context.fillStyle = 'rgba(0,0,0,0.7)'; - context.fill(); - context.closePath(); - - // draw dashed stroke rectangle - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 4 ]); - context.strokeRect(rect.left, rect.top, rect.width, rect.height); - - // Strange fix, but restore doesn't seem to reset the line dashes? - context.setLineDash([]); - - // draw the handles last, so they will be on top of the overlay - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - context.restore(); - } - ///////// END IMAGE RENDERING /////// - - // module exports - var preventHandleOutsideImage = true; - - cornerstoneTools.highlight = cornerstoneTools.mouseButtonRectangleTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - pointInsideRect: pointInsideRect, - toolType: toolType - }, preventHandleOutsideImage); - - cornerstoneTools.highlightTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - pointInsideRect: pointInsideRect, - toolType: toolType - }, preventHandleOutsideImage); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/highlight.js - -// Begin Source: src/imageTools/keyboardTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function keyboardTool(keyDownCallback) { - var configuration = {}; - - var toolInterface = { - activate: function(element) { - $(element).off('CornerstoneToolsKeyDown', keyDownCallback); - $(element).on('CornerstoneToolsKeyDown', keyDownCallback); - }, - disable: function(element) {$(element).off('CornerstoneToolsKeyDown', keyDownCallback);}, - enable: function(element) {$(element).off('CornerstoneToolsKeyDown', keyDownCallback);}, - deactivate: function(element) {$(element).off('CornerstoneToolsKeyDown', keyDownCallback);}, - getConfiguration: function() { return configuration; }, - setConfiguration: function(config) {configuration = config;} - }; - return toolInterface; - } - - // module exports - cornerstoneTools.keyboardTool = keyboardTool; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/keyboardTool.js - -// Begin Source: src/imageTools/lengthTool.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'length'; - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true - }, - textBox: { - active: false, - hasMoved: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - } - } - }; - - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - function pointNearTool(element, data, coords) { - var lineSegment = { - start: cornerstone.pixelToCanvas(element, data.handles.start), - end: cornerstone.pixelToCanvas(element, data.handles.end) - }; - var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - return (distanceToPoint < 25); - } - - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var config = cornerstoneTools.length.getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - - // configurable shadow - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - var data = toolData.data[i]; - var color = cornerstoneTools.toolColors.getColorIfActive(data.active); - - // Get the handle positions in canvas coordinates - var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end); - - // Draw the measurement line - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.moveTo(handleStartCanvas.x, handleStartCanvas.y); - context.lineTo(handleEndCanvas.x, handleEndCanvas.y); - context.stroke(); - - // Draw the handles - var handleOptions = { - drawHandlesIfActive: (config && config.drawHandlesOnHover) - }; - - cornerstoneTools.drawHandles(context, eventData, data.handles, color, handleOptions); - - // Draw the text - context.fillStyle = color; - - // Set rowPixelSpacing and columnPixelSpacing to 1 if they are undefined (or zero) - var dx = (data.handles.end.x - data.handles.start.x) * (eventData.image.columnPixelSpacing || 1); - var dy = (data.handles.end.y - data.handles.start.y) * (eventData.image.rowPixelSpacing || 1); - - // Calculate the length, and create the text variable with the millimeters or pixels suffix - var length = Math.sqrt(dx * dx + dy * dy); - - // Store the length inside the tool for outside access - data.length = length; - - // Set the length text suffix depending on whether or not pixelSpacing is available - var suffix = ' mm'; - if (!eventData.image.rowPixelSpacing || !eventData.image.columnPixelSpacing) { - suffix = ' pixels'; - } - - // Store the length measurement text - var text = '' + length.toFixed(2) + suffix; - - if (!data.handles.textBox.hasMoved) { - var coords = { - x: Math.max(data.handles.start.x, data.handles.end.x), - }; - - // Depending on which handle has the largest x-value, - // set the y-value for the text box - if (coords.x === data.handles.start.x) { - coords.y = data.handles.start.y; - } else { - coords.y = data.handles.end.y; - } - - data.handles.textBox.x = coords.x; - data.handles.textBox.y = coords.y; - } - - var textCoords = cornerstone.pixelToCanvas(eventData.element, data.handles.textBox); - - // Move the textbox slightly to the right and upwards - // so that it sits beside the length tool handle - textCoords.x += 10; - - var options = { - centering: { - x: false, - y: true - } - }; - - // Draw the textbox - var boundingBox = cornerstoneTools.drawTextBox(context, text, textCoords.x, textCoords.y, color, options); - data.handles.textBox.boundingBox = boundingBox; - - if (data.handles.textBox.hasMoved) { - // Draw dashed link line between ellipse and text - var link = { - start: {}, - end: {} - }; - - var midpointCanvas = { - x: (handleStartCanvas.x + handleEndCanvas.x) / 2, - y: (handleStartCanvas.y + handleEndCanvas.y) / 2, - }; - - var points = [ handleStartCanvas, handleEndCanvas, midpointCanvas ]; - - link.end.x = textCoords.x; - link.end.y = textCoords.y; - - link.start = cornerstoneMath.point.findClosestPoint(points, link.end); - - var boundingBoxPoints = [ { - // Top middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top - }, { - // Left middle point of bounding box - x: boundingBox.left, - y: boundingBox.top + boundingBox.height / 2 - }, { - // Bottom middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top + boundingBox.height - }, { - // Right middle point of bounding box - x: boundingBox.left + boundingBox.width, - y: boundingBox.top + boundingBox.height / 2 - }, - ]; - - link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); - - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 2, 3 ]); - context.moveTo(link.start.x, link.start.y); - context.lineTo(link.end.x, link.end.y); - context.stroke(); - } - - context.restore(); - } - } - ///////// END IMAGE RENDERING /////// - - // module exports - cornerstoneTools.length = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - - cornerstoneTools.lengthTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/lengthTool.js - -// Begin Source: src/imageTools/magnify.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var configuration = { - magnifySize: 100, - magnificationLevel: 2, - }; - - var browserName; - - var currentPoints; - - /** Remove the magnifying glass when the mouse event ends */ - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); - $(eventData.element).off('CornerstoneNewImage', newImageCallback); - hideTool(eventData); - } - - function hideTool(eventData) { - $(eventData.element).find('.magnifyTool').hide(); - // Re-enable the mouse cursor - document.body.style.cursor = 'default'; - } - - /** Draw the magnifying glass on mouseDown, and begin tracking mouse movements */ - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - $(eventData.element).on('CornerstoneToolsMouseDrag', eventData, dragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', eventData, mouseUpCallback); - $(eventData.element).on('CornerstoneToolsMouseClick', eventData, mouseUpCallback); - - currentPoints = eventData.currentPoints; - $(eventData.element).on('CornerstoneNewImage', eventData, newImageCallback); - drawMagnificationTool(eventData); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - function newImageCallback(e, eventData) { - eventData.currentPoints = currentPoints; - drawMagnificationTool(eventData); - } - - function dragEndCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsDragEnd', dragEndCallback); - $(eventData.element).off('CornerstoneToolsTouchEnd', dragEndCallback); - $(eventData.element).off('CornerstoneNewImage', newImageCallback); - hideTool(eventData); - } - - /** Drag callback is triggered by both the touch and mouse magnify tools */ - function dragCallback(e, eventData) { - currentPoints = eventData.currentPoints; - - drawMagnificationTool(eventData); - if (eventData.isTouchEvent === true) { - $(eventData.element).on('CornerstoneToolsDragEnd', dragEndCallback); - $(eventData.element).on('CornerstoneToolsTouchEnd', dragEndCallback); - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - /** Draws the magnifying glass */ - function drawMagnificationTool(eventData) { - var magnify = $(eventData.element).find('.magnifyTool').get(0); - - if (!magnify) { - createMagnificationCanvas(eventData.element); - } - - var config = cornerstoneTools.magnify.getConfiguration(); - - var magnifySize = config.magnifySize; - var magnificationLevel = config.magnificationLevel; - - // The 'not' magnifyTool class here is necessary because cornerstone places - // no classes of it's own on the canvas we want to select - var canvas = $(eventData.element).find('canvas').not('.magnifyTool').get(0); - var context = canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var zoomCtx = magnify.getContext('2d'); - zoomCtx.setTransform(1, 0, 0, 1, 0, 0); - - var getSize = magnifySize / magnificationLevel; - - // Calculate the on-canvas location of the mouse pointer / touch - var canvasLocation = cornerstone.pixelToCanvas(eventData.element, eventData.currentPoints.image); - - if (eventData.isTouchEvent === true) { - canvasLocation.y -= 1.25 * getSize; - } - - canvasLocation.x = Math.max(canvasLocation.x, 0); - canvasLocation.x = Math.min(canvasLocation.x, canvas.width); - - canvasLocation.y = Math.max(canvasLocation.y, 0); - canvasLocation.y = Math.min(canvasLocation.y, canvas.height); - - // Clear the rectangle - zoomCtx.clearRect(0, 0, magnifySize, magnifySize); - zoomCtx.fillStyle = 'transparent'; - - // Fill it with the pixels that the mouse is clicking on - zoomCtx.fillRect(0, 0, magnifySize, magnifySize); - - var copyFrom = { - x: canvasLocation.x - 0.5 * getSize, - y: canvasLocation.y - 0.5 * getSize - }; - - if (browserName === 'Safari') { - // Safari breaks when trying to copy pixels with negative indices - // This prevents proper Magnify usage - copyFrom.x = Math.max(copyFrom.x, 0); - copyFrom.y = Math.max(copyFrom.y, 0); - } - - copyFrom.x = Math.min(copyFrom.x, canvas.width); - copyFrom.y = Math.min(copyFrom.y, canvas.height); - - var scaledMagnify = { - x: (canvas.width - copyFrom.x) * magnificationLevel, - y: (canvas.height - copyFrom.y) * magnificationLevel - }; - zoomCtx.drawImage(canvas, copyFrom.x, copyFrom.y, canvas.width - copyFrom.x, canvas.height - copyFrom.y, 0, 0, scaledMagnify.x, scaledMagnify.y); - - // Place the magnification tool at the same location as the pointer - magnify.style.top = canvasLocation.y - 0.5 * magnifySize + 'px'; - magnify.style.left = canvasLocation.x - 0.5 * magnifySize + 'px'; - - magnify.style.display = 'block'; - - // Hide the mouse cursor, so the user can see better - document.body.style.cursor = 'none'; - } - - /** Creates the magnifying glass canvas */ - function createMagnificationCanvas(element) { - // If the magnifying glass canvas doesn't already exist - if ($(element).find('.magnifyTool').length === 0) { - // Create a canvas and append it as a child to the element - var magnify = document.createElement('canvas'); - // The magnifyTool class is used to find the canvas later on - magnify.classList.add('magnifyTool'); - - var config = cornerstoneTools.magnify.getConfiguration(); - magnify.width = config.magnifySize; - magnify.height = config.magnifySize; - - // Make sure position is absolute so the canvas can follow the mouse / touch - magnify.style.position = 'absolute'; - element.appendChild(magnify); - } - } - - /** Find the magnifying glass canvas and remove it */ - function removeMagnificationCanvas(element) { - $(element).find('.magnifyTool').remove(); - } - - // --- Mouse tool activate / disable --- // - function disable(element) { - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - removeMagnificationCanvas(element); - } - - function enable(element) { - var config = cornerstoneTools.magnify.getConfiguration(config); - - if (!browserName) { - var infoString = cornerstoneTools.getBrowserInfo(); - var info = infoString.split(' '); - browserName = info[0]; - } - - createMagnificationCanvas(element); - } - - function activate(element, mouseButtonMask) { - var eventData = { - mouseButtonMask: mouseButtonMask, - }; - - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - - $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); - createMagnificationCanvas(element); - } - - // --- Touch tool activate / disable --- // - function getConfiguration() { - return configuration; - } - - function setConfiguration(config) { - configuration = config; - } - - // module exports - cornerstoneTools.magnify = { - enable: enable, - activate: activate, - deactivate: disable, - disable: disable, - getConfiguration: getConfiguration, - setConfiguration: setConfiguration - }; - - var options = { - fireOnTouchStart: true, - activateCallback: createMagnificationCanvas, - disableCallback: removeMagnificationCanvas - }; - cornerstoneTools.magnifyTouchDrag = cornerstoneTools.touchDragTool(dragCallback, options); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/magnify.js - -// Begin Source: src/imageTools/multiTouchDragTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function multiTouchDragTool(touchDragCallback, options) { - var configuration = {}; - var events = 'CornerstoneToolsMultiTouchDrag'; - if (options && options.fireOnTouchStart === true) { - events += ' CornerstoneToolsMultiTouchStart'; - } - - var toolInterface = { - activate: function(element) { - $(element).off(events, touchDragCallback); - - if (options && options.eventData) { - $(element).on(events, options.eventData, touchDragCallback); - } else { - $(element).on(events, touchDragCallback); - } - - if (options && options.activateCallback) { - options.activateCallback(element); - } - }, - disable: function(element) { - $(element).off(events, touchDragCallback); - if (options && options.disableCallback) { - options.disableCallback(element); - } - }, - enable: function(element) { - $(element).off(events, touchDragCallback); - if (options && options.enableCallback) { - options.enableCallback(element); - } - }, - deactivate: function(element) { - $(element).off(events, touchDragCallback); - if (options && options.deactivateCallback) { - options.deactivateCallback(element); - } - }, - getConfiguration: function() { - return configuration; - }, - setConfiguration: function(config) { - configuration = config; - } - }; - return toolInterface; - } - - // module exports - cornerstoneTools.multiTouchDragTool = multiTouchDragTool; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/multiTouchDragTool.js - -// Begin Source: src/imageTools/orientationMarkers.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function getOrientationMarkers(element) { - var enabledElement = cornerstone.getEnabledElement(element); - var imagePlaneMetaData = cornerstoneTools.metaData.get('imagePlane', enabledElement.image.imageId); - - if (!imagePlaneMetaData || !imagePlaneMetaData.rowCosines || !imagePlaneMetaData.columnCosines) { - return; - } - - var rowString = cornerstoneTools.orientation.getOrientationString(imagePlaneMetaData.rowCosines); - var columnString = cornerstoneTools.orientation.getOrientationString(imagePlaneMetaData.columnCosines); - - var oppositeRowString = cornerstoneTools.orientation.invertOrientationString(rowString); - var oppositeColumnString = cornerstoneTools.orientation.invertOrientationString(columnString); - - return { - top: oppositeColumnString, - bottom: columnString, - left: oppositeRowString, - right: rowString - }; - } - - function getOrientationMarkerPositions(element) { - var enabledElement = cornerstone.getEnabledElement(element); - var coords; - - coords = { - x: enabledElement.image.width / 2, - y: 5 - }; - var topCoords = cornerstone.pixelToCanvas(element, coords); - - coords = { - x: enabledElement.image.width / 2, - y: enabledElement.image.height - 5 - }; - var bottomCoords = cornerstone.pixelToCanvas(element, coords); - - coords = { - x: 5, - y: enabledElement.image.height / 2 - }; - var leftCoords = cornerstone.pixelToCanvas(element, coords); - - coords = { - x: enabledElement.image.width - 10, - y: enabledElement.image.height / 2 - }; - var rightCoords = cornerstone.pixelToCanvas(element, coords); - - return { - top: topCoords, - bottom: bottomCoords, - left: leftCoords, - right: rightCoords - }; - } - - function onImageRendered(e, eventData) { - var element = eventData.element; - - var markers = getOrientationMarkers(element); - - if (!markers) { - return; - } - - var coords = getOrientationMarkerPositions(element, markers); - - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color = cornerstoneTools.toolColors.getToolColor(); - - var textWidths = { - top: context.measureText(markers.top).width, - left: context.measureText(markers.left).width, - right: context.measureText(markers.right).width, - bottom: context.measureText(markers.bottom).width - }; - - cornerstoneTools.drawTextBox(context, markers.top, coords.top.x - textWidths.top / 2, coords.top.y, color); - cornerstoneTools.drawTextBox(context, markers.left, coords.left.x - textWidths.left / 2, coords.left.y, color); - - var config = cornerstoneTools.orientationMarkers.getConfiguration(); - if (config && config.drawAllMarkers) { - cornerstoneTools.drawTextBox(context, markers.right, coords.right.x - textWidths.right / 2, coords.right.y, color); - cornerstoneTools.drawTextBox(context, markers.bottom, coords.bottom.x - textWidths.bottom / 2, coords.bottom.y, color); - } - } - ///////// END IMAGE RENDERING /////// - - // module exports - cornerstoneTools.orientationMarkers = cornerstoneTools.displayTool(onImageRendered); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/orientationMarkers.js - -// Begin Source: src/imageTools/pan.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - $(eventData.element).on('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).on('CornerstoneToolsMouseClick', mouseUpCallback); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - function dragCallback(e, eventData) { - - // FIXME: Copied from Cornerstone src/internal/calculateTransform.js, should be exposed from there. - var widthScale = eventData.viewport.scale; - var heightScale = eventData.viewport.scale; - if (eventData.image.rowPixelSpacing < eventData.image.columnPixelSpacing) { - widthScale = widthScale * (eventData.image.columnPixelSpacing / eventData.image.rowPixelSpacing); - } else if (eventData.image.columnPixelSpacing < eventData.image.rowPixelSpacing) { - heightScale = heightScale * (eventData.image.rowPixelSpacing / eventData.image.columnPixelSpacing); - } - - eventData.viewport.translation.x += (eventData.deltaPoints.page.x / widthScale); - eventData.viewport.translation.y += (eventData.deltaPoints.page.y / heightScale); - cornerstone.setViewport(eventData.element, eventData.viewport); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - cornerstoneTools.pan = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); - cornerstoneTools.panTouchDrag = cornerstoneTools.touchDragTool(dragCallback); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/pan.js - -// Begin Source: src/imageTools/panMultiTouch.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function touchPanCallback(e, eventData) { - var config = cornerstoneTools.panMultiTouch.getConfiguration(); - if (config && config.testPointers(eventData)) { - eventData.viewport.translation.x += (eventData.deltaPoints.page.x / eventData.viewport.scale); - eventData.viewport.translation.y += (eventData.deltaPoints.page.y / eventData.viewport.scale); - cornerstone.setViewport(eventData.element, eventData.viewport); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - var configuration = { - testPointers: function(eventData) { - return (eventData.numPointers >= 2); - } - }; - - cornerstoneTools.panMultiTouch = cornerstoneTools.multiTouchDragTool(touchPanCallback); - cornerstoneTools.panMultiTouch.setConfiguration(configuration); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/panMultiTouch.js - -// Begin Source: src/imageTools/probe.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var toolType = 'probe'; - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - handles: { - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true - } - } - }; - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - ///////// BEGIN IMAGE RENDERING /////// - function pointNearTool(element, data, coords) { - var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - return cornerstoneMath.point.distance(endCanvas, coords) < 5; - } - - function onImageRendered(e, eventData) { - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color; - var font = cornerstoneTools.textStyle.getFont(); - var fontHeight = cornerstoneTools.textStyle.getFontSize(); - - for (var i = 0; i < toolData.data.length; i++) { - - context.save(); - var data = toolData.data[i]; - - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } - - // draw the handles - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - - var x = Math.round(data.handles.end.x); - var y = Math.round(data.handles.end.y); - var storedPixels; - - var text, - str; - - if (x < 0 || y < 0 || x >= eventData.image.columns || y >= eventData.image.rows) { - return; - } - - if (eventData.image.color) { - text = '' + x + ', ' + y; - storedPixels = cornerstoneTools.getRGBPixels(eventData.element, x, y, 1, 1); - str = 'R: ' + storedPixels[0] + ' G: ' + storedPixels[1] + ' B: ' + storedPixels[2]; - } else { - storedPixels = cornerstone.getStoredPixels(eventData.element, x, y, 1, 1); - var sp = storedPixels[0]; - var mo = sp * eventData.image.slope + eventData.image.intercept; - var suv = cornerstoneTools.calculateSUV(eventData.image, sp); - - // Draw text - text = '' + x + ', ' + y; - str = 'SP: ' + sp + ' MO: ' + parseFloat(mo.toFixed(3)); - if (suv) { - str += ' SUV: ' + parseFloat(suv.toFixed(3)); - } - } - - var coords = { - // translate the x/y away from the cursor - x: data.handles.end.x + 3, - y: data.handles.end.y - 3 - }; - var textCoords = cornerstone.pixelToCanvas(eventData.element, coords); - - context.font = font; - context.fillStyle = color; - - cornerstoneTools.drawTextBox(context, str, textCoords.x, textCoords.y + fontHeight + 5, color); - cornerstoneTools.drawTextBox(context, text, textCoords.x, textCoords.y, color); - context.restore(); - } - } - ///////// END IMAGE RENDERING /////// - - // module exports - cornerstoneTools.probe = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - cornerstoneTools.probeTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/probe.js - -// Begin Source: src/imageTools/rectangleRoi.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'rectangleRoi'; - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - invalidated: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true - }, - textBox: { - active: false, - hasMoved: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - } - } - }; - - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - function pointNearTool(element, data, coords) { - var startCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - - var rect = { - left: Math.min(startCanvas.x, endCanvas.x), - top: Math.min(startCanvas.y, endCanvas.y), - width: Math.abs(startCanvas.x - endCanvas.x), - height: Math.abs(startCanvas.y - endCanvas.y) - }; - - var distanceToPoint = cornerstoneMath.rect.distanceToPoint(rect, coords); - return (distanceToPoint < 5); - } - - ///////// BEGIN IMAGE RENDERING /////// - - function calculateMeanStdDev(sp, ellipse) { - // TODO: Get a real statistics library here that supports large counts - - var sum = 0; - var sumSquared = 0; - var count = 0; - var index = 0; - - for (var y = ellipse.top; y < ellipse.top + ellipse.height; y++) { - for (var x = ellipse.left; x < ellipse.left + ellipse.width; x++) { - sum += sp[index]; - sumSquared += sp[index] * sp[index]; - count++; - index++; - } - } - - if (count === 0) { - return { - count: count, - mean: 0.0, - variance: 0.0, - stdDev: 0.0 - }; - } - - var mean = sum / count; - var variance = sumSquared / count - mean * mean; - - return { - count: count, - mean: mean, - variance: variance, - stdDev: Math.sqrt(variance) - }; - } - - function numberWithCommas(x) { - // http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript - var parts = x.toString().split('.'); - parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); - return parts.join('.'); - } - - function onImageRendered(e, eventData) { - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - var image = eventData.image; - var element = eventData.element; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var config = cornerstoneTools.rectangleRoi.getConfiguration(); - var context = eventData.canvasContext.canvas.getContext('2d'); - var seriesModule = cornerstone.metaData.get('generalSeriesModule', image.imageId); - var modality; - if (seriesModule) { - modality = seriesModule.modality; - } - - context.setTransform(1, 0, 0, 1, 0, 0); - - // If we have tool data for this element - iterate over each set and draw it - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - - var data = toolData.data[i]; - - // Apply any shadow settings defined in the tool configuration - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - // Check which color the rendered tool should be - var color = cornerstoneTools.toolColors.getColorIfActive(data.active); - - // Convert Image coordinates to Canvas coordinates given the element - var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - - // Retrieve the bounds of the ellipse (left, top, width, and height) - // in Canvas coordinates - var leftCanvas = Math.min(handleStartCanvas.x, handleEndCanvas.x); - var topCanvas = Math.min(handleStartCanvas.y, handleEndCanvas.y); - var widthCanvas = Math.abs(handleStartCanvas.x - handleEndCanvas.x); - var heightCanvas = Math.abs(handleStartCanvas.y - handleEndCanvas.y); - - // Draw the rectangle on the canvas - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.rect(leftCanvas, topCanvas, widthCanvas, heightCanvas); - context.stroke(); - - // If the tool configuration specifies to only draw the handles on hover / active, - // follow this logic - if (config && config.drawHandlesOnHover) { - // Draw the handles if the tool is active - if (data.active === true) { - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - } else { - // If the tool is inactive, draw the handles only if each specific handle is being - // hovered over - var handleOptions = { - drawHandlesIfActive: true - }; - cornerstoneTools.drawHandles(context, eventData, data.handles, color, handleOptions); - } - } else { - // If the tool has no configuration settings, always draw the handles - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - } - - // Define variables for the area and mean/standard deviation - var area, - meanStdDev, - meanStdDevSUV; - - // Perform a check to see if the tool has been invalidated. This is to prevent - // unnecessary re-calculation of the area, mean, and standard deviation if the - // image is re-rendered but the tool has not moved (e.g. during a zoom) - if (!data.invalidated) { - // If the data is not invalidated, retrieve it from the toolData - meanStdDev = data.meanStdDev; - meanStdDevSUV = data.meanStdDevSUV; - area = data.area; - } else { - // If the data has been invalidated, we need to calculate it again - - // Retrieve the bounds of the ellipse in image coordinates - var ellipse = { - left: Math.min(data.handles.start.x, data.handles.end.x), - top: Math.min(data.handles.start.y, data.handles.end.y), - width: Math.abs(data.handles.start.x - data.handles.end.x), - height: Math.abs(data.handles.start.y - data.handles.end.y) - }; - - // First, make sure this is not a color image, since no mean / standard - // deviation will be calculated for color images. - if (!image.color) { - // Retrieve the array of pixels that the ellipse bounds cover - var pixels = cornerstone.getPixels(element, ellipse.left, ellipse.top, ellipse.width, ellipse.height); - - // Calculate the mean & standard deviation from the pixels and the ellipse details - meanStdDev = calculateMeanStdDev(pixels, ellipse); - - if (modality === 'PT') { - // If the image is from a PET scan, use the DICOM tags to - // calculate the SUV from the mean and standard deviation. - - // Note that because we are using modality pixel values from getPixels, and - // the calculateSUV routine also rescales to modality pixel values, we are first - // returning the values to storedPixel values before calcuating SUV with them. - // TODO: Clean this up? Should we add an option to not scale in calculateSUV? - meanStdDevSUV = { - mean: cornerstoneTools.calculateSUV(image, (meanStdDev.mean - image.intercept) / image.slope), - stdDev: cornerstoneTools.calculateSUV(image, (meanStdDev.stdDev - image.intercept) / image.slope) - }; - } - - // If the mean and standard deviation values are sane, store them for later retrieval - if (meanStdDev && !isNaN(meanStdDev.mean)) { - data.meanStdDev = meanStdDev; - data.meanStdDevSUV = meanStdDevSUV; - } - } - - // Retrieve the pixel spacing values, and if they are not - // real non-zero values, set them to 1 - var columnPixelSpacing = image.columnPixelSpacing || 1; - var rowPixelSpacing = image.rowPixelSpacing || 1; - - // Calculate the image area from the ellipse dimensions and pixel spacing - area = (ellipse.width * columnPixelSpacing) * (ellipse.height * rowPixelSpacing); - - // If the area value is sane, store it for later retrieval - if (!isNaN(area)) { - data.area = area; - } - - // Set the invalidated flag to false so that this data won't automatically be recalculated - data.invalidated = false; - } - - // Define an array to store the rows of text for the textbox - var textLines = []; - - // If the mean and standard deviation values are present, display them - if (meanStdDev && meanStdDev.mean) { - // If the modality is CT, add HU to denote Hounsfield Units - var moSuffix = ''; - if (modality === 'CT') { - moSuffix = ' HU'; - } - - // Create a line of text to display the mean and any units that were specified (i.e. HU) - var meanText = 'Mean: ' + numberWithCommas(meanStdDev.mean.toFixed(2)) + moSuffix; - // Create a line of text to display the standard deviation and any units that were specified (i.e. HU) - var stdDevText = 'StdDev: ' + numberWithCommas(meanStdDev.stdDev.toFixed(2)) + moSuffix; - - // If this image has SUV values to display, concatenate them to the text line - if (meanStdDevSUV && meanStdDevSUV.mean !== undefined) { - var SUVtext = ' SUV: '; - meanText += SUVtext + numberWithCommas(meanStdDevSUV.mean.toFixed(2)); - stdDevText += SUVtext + numberWithCommas(meanStdDevSUV.stdDev.toFixed(2)); - } - - // Add these text lines to the array to be displayed in the textbox - textLines.push(meanText); - textLines.push(stdDevText); - } - - // If the area is a sane value, display it - if (area) { - // Determine the area suffix based on the pixel spacing in the image. - // If pixel spacing is present, use millimeters. Otherwise, use pixels. - // This uses Char code 178 for a superscript 2 - var suffix = ' mm' + String.fromCharCode(178); - if (!image.rowPixelSpacing || !image.columnPixelSpacing) { - suffix = ' pixels' + String.fromCharCode(178); - } - - // Create a line of text to display the area and its units - var areaText = 'Area: ' + numberWithCommas(area.toFixed(2)) + suffix; - - // Add this text line to the array to be displayed in the textbox - textLines.push(areaText); - } - - // If the textbox has not been moved by the user, it should be displayed on the right-most - // side of the tool. - if (!data.handles.textBox.hasMoved) { - // Find the rightmost side of the ellipse at its vertical center, and place the textbox here - // Note that this calculates it in image coordinates - data.handles.textBox.x = Math.max(data.handles.start.x, data.handles.end.x); - data.handles.textBox.y = (data.handles.start.y + data.handles.end.y) / 2; - } - - // Convert the textbox Image coordinates into Canvas coordinates - var textCoords = cornerstone.pixelToCanvas(element, data.handles.textBox); - - // Set options for the textbox drawing function - var options = { - centering: { - x: false, - y: true - } - }; - - // Draw the textbox and retrieves it's bounding box for mouse-dragging and highlighting - var boundingBox = cornerstoneTools.drawTextBox(context, textLines, textCoords.x, - textCoords.y, color, options); - - // Store the bounding box data in the handle for mouse-dragging and highlighting - data.handles.textBox.boundingBox = boundingBox; - - // If the textbox has moved, we would like to draw a line linking it with the tool - // This section decides where to draw this line to on the Ellipse based on the location - // of the textbox relative to the ellipse. - if (data.handles.textBox.hasMoved) { - // Draw dashed link line between tool and text - - // The initial link position is at the center of the - // textbox. - var link = { - start: {}, - end: { - x: textCoords.x, - y: textCoords.y - } - }; - - // First we calculate the ellipse points (top, left, right, and bottom) - var ellipsePoints = [ { - // Top middle point of ellipse - x: leftCanvas + widthCanvas / 2, - y: topCanvas - }, { - // Left middle point of ellipse - x: leftCanvas, - y: topCanvas + heightCanvas / 2 - }, { - // Bottom middle point of ellipse - x: leftCanvas + widthCanvas / 2, - y: topCanvas + heightCanvas - }, { - // Right middle point of ellipse - x: leftCanvas + widthCanvas, - y: topCanvas + heightCanvas / 2 - } ]; - - // We obtain the link starting point by finding the closest point on the ellipse to the - // center of the textbox - link.start = cornerstoneMath.point.findClosestPoint(ellipsePoints, link.end); - - // Next we calculate the corners of the textbox bounding box - var boundingBoxPoints = [ { - // Top middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top - }, { - // Left middle point of bounding box - x: boundingBox.left, - y: boundingBox.top + boundingBox.height / 2 - }, { - // Bottom middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top + boundingBox.height - }, { - // Right middle point of bounding box - x: boundingBox.left + boundingBox.width, - y: boundingBox.top + boundingBox.height / 2 - }, ]; - - // Now we recalculate the link endpoint by identifying which corner of the bounding box - // is closest to the start point we just calculated. - link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); - - // Finally we draw the dashed linking line - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 2, 3 ]); - context.moveTo(link.start.x, link.start.y); - context.lineTo(link.end.x, link.end.y); - context.stroke(); - } - - context.restore(); - } - } - ///////// END IMAGE RENDERING /////// - - // module exports - cornerstoneTools.rectangleRoi = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - cornerstoneTools.rectangleRoiTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/rectangleRoi.js - -// Begin Source: src/imageTools/rotate.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // --- Strategies --- // - function defaultStrategy(eventData) { - // Calculate distance from the center of the image - var rect = eventData.element.getBoundingClientRect(eventData.element); - - var points = { - x: eventData.currentPoints.client.x, - y: eventData.currentPoints.client.y - }; - - var width = eventData.element.clientWidth; - var height = eventData.element.clientHeight; - - var pointsFromCenter = { - x: points.x - rect.left - width / 2, - // Invert the coordinate system so that up is positive - y: -1 * (points.y - rect.top - height / 2) - }; - - var rotationRadians = Math.atan2(pointsFromCenter.y, pointsFromCenter.x); - var rotationDegrees = rotationRadians * (180 / Math.PI); - var rotation = -1 * rotationDegrees + 90; - eventData.viewport.rotation = rotation; - cornerstone.setViewport(eventData.element, eventData.viewport); - } - - function horizontalStrategy(eventData) { - eventData.viewport.rotation += (eventData.deltaPoints.page.x / eventData.viewport.scale); - cornerstone.setViewport(eventData.element, eventData.viewport); - } - - function verticalStrategy(eventData) { - eventData.viewport.rotation += (eventData.deltaPoints.page.y / eventData.viewport.scale); - cornerstone.setViewport(eventData.element, eventData.viewport); - } - - // --- Mouse event callbacks --- // - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - $(eventData.element).on('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).on('CornerstoneToolsMouseClick', mouseUpCallback); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - function dragCallback(e, eventData) { - cornerstoneTools.rotate.strategy(eventData); - cornerstone.setViewport(eventData.element, eventData.viewport); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - cornerstoneTools.rotate = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); - cornerstoneTools.rotate.strategies = { - default: defaultStrategy, - horizontal: horizontalStrategy, - vertical: verticalStrategy - }; - - cornerstoneTools.rotate.strategy = defaultStrategy; - - cornerstoneTools.rotateTouchDrag = cornerstoneTools.touchDragTool(dragCallback); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/rotate.js - -// Begin Source: src/imageTools/rotateTouch.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function touchRotateCallback(e, eventData) { - eventData.viewport.rotation += eventData.rotation; - cornerstone.setViewport(eventData.element, eventData.viewport); - return false; - } - - function disable(element) { - $(element).off('CornerstoneToolsTouchRotate', touchRotateCallback); - } - - function activate(element) { - $(element).off('CornerstoneToolsTouchRotate', touchRotateCallback); - $(element).on('CornerstoneToolsTouchRotate', touchRotateCallback); - } - - cornerstoneTools.rotateTouch = { - activate: activate, - disable: disable - }; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/rotateTouch.js - -// Begin Source: src/imageTools/saveImage.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function saveAs(element, filename) { - var canvas = $(element).find('canvas').get(0); - - // Thanks to Ken Fyrstenber - // http://stackoverflow.com/questions/18480474/how-to-save-an-image-from-canvas - var lnk = document.createElement('a'); - - /// the key here is to set the download attribute of the a tag - lnk.download = filename; - - /// convert canvas content to data-uri for link. When download - /// attribute is set the content pointed to by link will be - /// pushed as 'download' in HTML5 capable browsers - lnk.href = canvas.toDataURL(); - - /// create a 'fake' click-event to trigger the download - if (document.createEvent) { - - var e = document.createEvent('MouseEvents'); - e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); - - lnk.dispatchEvent(e); - - } else if (lnk.fireEvent) { - - lnk.fireEvent('onclick'); - } - } - - cornerstoneTools.saveAs = saveAs; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/saveImage.js - -// Begin Source: src/imageTools/seedAnnotate.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'seedAnnotate'; - - // Define a callback to get your text annotation - // This could be used, e.g. to open a modal - function getTextCallback(doneGetTextCallback) { - doneGetTextCallback(prompt('Enter your annotation:')); - } - - function changeTextCallback(data, eventData, doneChangingTextCallback) { - doneChangingTextCallback(prompt('Change your annotation:')); - } - - var configuration = { - getTextCallback: getTextCallback, - changeTextCallback: changeTextCallback, - drawHandles: false, - drawHandlesOnHover: true, - currentLetter: 'A', - currentNumber: 0, - showCoordinates: true, - countUp: true - }; - /// --- Mouse Tool --- /// - - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurement(mouseEventData) { - var element = mouseEventData.element; - var config = cornerstoneTools.seedAnnotate.getConfiguration(); - var measurementData = createNewMeasurement(mouseEventData); - - function doneGetTextCallback(text) { - if (text !== null) { - measurementData.text = text; - } else { - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - measurementData.active = false; - cornerstone.updateImage(element); - } - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(element, toolType, measurementData); - - cornerstone.updateImage(element); - cornerstoneTools.moveHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { - if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - if (measurementData.text === undefined) { - config.getTextCallback(doneGetTextCallback); - } - - cornerstone.updateImage(element); - }); - } - - function createNewMeasurement(mouseEventData) { - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - handles: { - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - textBox: { - active: false, - hasMoved: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - } - } - }; - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - function pointNearTool(element, data, coords) { - if (!data.handles.end) { - return; - } - - var realCoords = cornerstone.pixelToCanvas(element, data.handles.end); - var distanceToPoint = cornerstoneMath.point.distance(realCoords, coords); - return (distanceToPoint < 25); - } - - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - var enabledElement = eventData.enabledElement; - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - // We need the canvas width - var canvasWidth = eventData.canvasContext.canvas.width; - - var color; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var font = cornerstoneTools.textStyle.getFont(); - var config = cornerstoneTools.seedAnnotate.getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - var data = toolData.data[i]; - - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } - - // Draw - var handleCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end); - - // Draw the circle always at the end of the handle - cornerstoneTools.drawCircle(context, handleCanvas, color, lineWidth); - - var handleOptions = { - drawHandlesIfActive: (config && config.drawHandlesOnHover) - }; - - if (config.drawHandles) { - cornerstoneTools.drawHandles(context, eventData, handleCanvas, color, handleOptions); - } - - // Draw the text - if (data.text && data.text !== '') { - context.font = font; - - var textPlusCoords = ''; - - if ( config.showCoordinates ) { - textPlusCoords = data.text + ' x: ' + Math.round(data.handles.end.x) + - ' y: ' + Math.round(data.handles.end.y); - } else { - textPlusCoords = data.text; - } - - // Calculate the text coordinates. - var textWidth = context.measureText(textPlusCoords).width + 10; - var textHeight = cornerstoneTools.textStyle.getFontSize() + 10; - - var distance = Math.max(textWidth, textHeight) / 2 + 5; - if (handleCanvas.x > (canvasWidth / 2)) { - distance = -distance; - } - - var textCoords; - if (!data.handles.textBox.hasMoved) { - textCoords = { - x: handleCanvas.x - textWidth / 2 + distance, - y: handleCanvas.y - textHeight / 2 - }; - - var transform = cornerstone.internal.getTransform(enabledElement); - transform.invert(); - - var coords = transform.transformPoint(textCoords.x, textCoords.y); - data.handles.textBox.x = coords.x; - data.handles.textBox.y = coords.y; - } - - textCoords = cornerstone.pixelToCanvas(eventData.element, data.handles.textBox); - - var boundingBox = cornerstoneTools.drawTextBox(context, textPlusCoords, textCoords.x, textCoords.y, color); - data.handles.textBox.boundingBox = boundingBox; - - if (data.handles.textBox.hasMoved) { - // Draw dashed link line between tool and text - var link = { - start: {}, - end: {} - }; - - link.end.x = textCoords.x; - link.end.y = textCoords.y; - - link.start = handleCanvas; - - var boundingBoxPoints = [ - { - // Top middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top - }, { - // Left middle point of bounding box - x: boundingBox.left, - y: boundingBox.top + boundingBox.height / 2 - }, { - // Bottom middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top + boundingBox.height - }, { - // Right middle point of bounding box - x: boundingBox.left + boundingBox.width, - y: boundingBox.top + boundingBox.height / 2 - }, - ]; - - link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); - - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 2, 3 ]); - context.moveTo(link.start.x, link.start.y); - context.lineTo(link.end.x, link.end.y); - context.stroke(); - } - } - - context.restore(); - } - } - // ---- Touch tool ---- - - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurementTouch(touchEventData) { - var element = touchEventData.element; - var config = cornerstoneTools.seedAnnotate.getConfiguration(); - var measurementData = createNewMeasurement(touchEventData); - - function doneGetTextCallback(text) { - if (text !== null) { - measurementData.text = text; - } else { - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - measurementData.active = false; - cornerstone.updateImage(element); - } - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(element, toolType, measurementData); - - cornerstone.updateImage(element); - cornerstoneTools.moveHandle(touchEventData, toolType, measurementData, measurementData.handles.end, function() { - if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - if (measurementData.text === undefined) { - config.getTextCallback(doneGetTextCallback); - } - - cornerstone.updateImage(element); - }); - } - - function doubleClickCallback(e, eventData) { - var element = eventData.element; - var data; - - function doneChangingTextCallback(data, updatedText, deleteTool) { - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - } else { - data.text = updatedText; - } - - data.active = false; - cornerstone.updateImage(element); - } - - if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - return; - } - - var config = cornerstoneTools.seedAnnotate.getConfiguration(); - - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); - - // now check to see if there is a handle we can move - if (!toolData) { - return; - } - - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords) || - cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { - - data.active = true; - cornerstone.updateImage(element); - // Allow relabelling via a callback - config.changeTextCallback(data, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - function pressCallback(e, eventData) { - var element = eventData.element; - var data; - - function doneChangingTextCallback(data, updatedText, deleteTool) { - console.log('pressCallback doneChangingTextCallback'); - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - } else { - data.text = updatedText; - } - - data.active = false; - cornerstone.updateImage(element); - - $(element).on('CornerstoneToolsTouchStart', cornerstoneTools.seedAnnotateTouch.touchStartCallback); - $(element).on('CornerstoneToolsTouchStartActive', cornerstoneTools.seedAnnotateTouch.touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', cornerstoneTools.seedAnnotateTouch.tapCallback); - } - - if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - return false; - } - - var config = cornerstoneTools.seedAnnotate.getConfiguration(); - - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); - - // now check to see if there is a handle we can move - if (!toolData) { - return false; - } - - if (eventData.handlePressed) { - $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.seedAnnotateTouch.touchStartCallback); - $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.seedAnnotateTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.seedAnnotateTouch.tapCallback); - - // Allow relabelling via a callback - config.changeTextCallback(eventData.handlePressed, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords) || - cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { - data.active = true; - cornerstone.updateImage(element); - - $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.seedAnnotateTouch.touchStartCallback); - $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.seedAnnotateTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.seedAnnotateTouch.tapCallback); - - // Allow relabelling via a callback - config.changeTextCallback(data, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - cornerstoneTools.seedAnnotate = cornerstoneTools.mouseButtonTool({ - addNewMeasurement: addNewMeasurement, - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType, - mouseDoubleClickCallback: doubleClickCallback - }); - - cornerstoneTools.seedAnnotate.setConfiguration(configuration); - - cornerstoneTools.seedAnnotateTouch = cornerstoneTools.touchTool({ - addNewMeasurement: addNewMeasurementTouch, - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType, - pressCallback: pressCallback - }); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/seedAnnotate.js - -// Begin Source: src/imageTools/simpleAngle.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'simpleAngle'; - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - // create the measurement data for this tool with the end handle activated - var angleData = { - visible: true, - active: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - middle: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false - }, - textBox: { - active: false, - hasMoved: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - } - } - }; - - return angleData; - } - ///////// END ACTIVE TOOL /////// - - function pointNearTool(element, data, coords) { - var lineSegment = { - start: cornerstone.pixelToCanvas(element, data.handles.start), - end: cornerstone.pixelToCanvas(element, data.handles.middle) - }; - - var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - if (distanceToPoint < 25) { - return true; - } - - lineSegment.start = cornerstone.pixelToCanvas(element, data.handles.middle); - lineSegment.end = cornerstone.pixelToCanvas(element, data.handles.end); - - distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - return (distanceToPoint < 25); - } - - function length(vector) { - return Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2)); - } - - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - var enabledElement = eventData.enabledElement; - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - //activation color - var color; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var font = cornerstoneTools.textStyle.getFont(); - var config = cornerstoneTools.simpleAngle.getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - var data = toolData.data[i]; - - //differentiate the color of activation tool - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } - - var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start); - var handleMiddleCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.middle); - var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end); - - // draw the line - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.moveTo(handleStartCanvas.x, handleStartCanvas.y); - context.lineTo(handleMiddleCanvas.x, handleMiddleCanvas.y); - context.lineTo(handleEndCanvas.x, handleEndCanvas.y); - context.stroke(); - - // draw the handles - var handleOptions = { - drawHandlesIfActive: (config && config.drawHandlesOnHover) - }; - - cornerstoneTools.drawHandles(context, eventData, data.handles, color, handleOptions); - - // Draw the text - context.fillStyle = color; - - // Default to isotropic pixel size, update suffix to reflect this - var columnPixelSpacing = eventData.image.columnPixelSpacing || 1; - var rowPixelSpacing = eventData.image.rowPixelSpacing || 1; - var suffix = ''; - if (!eventData.image.rowPixelSpacing || !eventData.image.columnPixelSpacing) { - suffix = ' (isotropic)'; - } - - var sideA = { - x: (Math.ceil(data.handles.middle.x) - Math.ceil(data.handles.start.x)) * columnPixelSpacing, - y: (Math.ceil(data.handles.middle.y) - Math.ceil(data.handles.start.y)) * rowPixelSpacing - }; - - var sideB = { - x: (Math.ceil(data.handles.end.x) - Math.ceil(data.handles.middle.x)) * columnPixelSpacing, - y: (Math.ceil(data.handles.end.y) - Math.ceil(data.handles.middle.y)) * rowPixelSpacing - }; - - var sideC = { - x: (Math.ceil(data.handles.end.x) - Math.ceil(data.handles.start.x)) * columnPixelSpacing, - y: (Math.ceil(data.handles.end.y) - Math.ceil(data.handles.start.y)) * rowPixelSpacing - }; - - var sideALength = length(sideA); - var sideBLength = length(sideB); - var sideCLength = length(sideC); - - // Cosine law - var angle = Math.acos((Math.pow(sideALength, 2) + Math.pow(sideBLength, 2) - Math.pow(sideCLength, 2)) / (2 * sideALength * sideBLength)); - angle = angle * (180 / Math.PI); - - var rAngle = cornerstoneTools.roundToDecimal(angle, 2); - - if (rAngle) { - var str = '00B0'; // degrees symbol - var text = rAngle.toString() + String.fromCharCode(parseInt(str, 16)) + suffix; - - var distance = 15; - - var textCoords; - if (!data.handles.textBox.hasMoved) { - textCoords = { - x: handleMiddleCanvas.x, - y: handleMiddleCanvas.y - }; - - context.font = font; - var textWidth = context.measureText(text).width; - if (handleMiddleCanvas.x < handleStartCanvas.x) { - textCoords.x -= distance + textWidth + 10; - } else { - textCoords.x += distance; - } - - var transform = cornerstone.internal.getTransform(enabledElement); - transform.invert(); - - var coords = transform.transformPoint(textCoords.x, textCoords.y); - data.handles.textBox.x = coords.x; - data.handles.textBox.y = coords.y; - - } else { - textCoords = cornerstone.pixelToCanvas(eventData.element, data.handles.textBox); - } - - var options = { - centering: { - x: false, - y: true - } - }; - - var boundingBox = cornerstoneTools.drawTextBox(context, text, textCoords.x, textCoords.y, color, options); - data.handles.textBox.boundingBox = boundingBox; - - if (data.handles.textBox.hasMoved) { - // Draw dashed link line between tool and text - var link = { - start: {}, - end: {} - }; - - var points = [ handleStartCanvas, handleEndCanvas, handleMiddleCanvas ]; - - link.end.x = textCoords.x; - link.end.y = textCoords.y; - - link.start = cornerstoneMath.point.findClosestPoint(points, link.end); - - var boundingBoxPoints = [ { - // Top middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top - }, { - // Left middle point of bounding box - x: boundingBox.left, - y: boundingBox.top + boundingBox.height / 2 - }, { - // Bottom middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top + boundingBox.height - }, { - // Right middle point of bounding box - x: boundingBox.left + boundingBox.width, - y: boundingBox.top + boundingBox.height / 2 - }, - ]; - - link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); - - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 2, 3 ]); - context.moveTo(link.start.x, link.start.y); - context.lineTo(link.end.x, link.end.y); - context.stroke(); - } - } - - context.restore(); - } - } - ///////// END IMAGE RENDERING /////// - - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurement(mouseEventData) { - var measurementData = createNewMeasurement(mouseEventData); - var element = mouseEventData.element; - - var eventData = { - mouseButtonMask: mouseEventData.which, - }; - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(element, toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(element).off('CornerstoneToolsMouseMove', cornerstoneTools.simpleAngle.mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDrag', cornerstoneTools.simpleAngle.mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', cornerstoneTools.simpleAngle.mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.simpleAngle.mouseDownActivateCallback); - cornerstone.updateImage(element); - - cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.middle, function() { - measurementData.active = false; - if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - - $(element).on('CornerstoneToolsMouseMove', cornerstoneTools.simpleAngle.mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDrag', cornerstoneTools.simpleAngle.mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.simpleAngle.mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.simpleAngle.mouseDownActivateCallback); - cornerstone.updateImage(element); - return; - } - - measurementData.handles.end.active = true; - cornerstone.updateImage(element); - - cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { - measurementData.active = false; - if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - $(element).on('CornerstoneToolsMouseMove', cornerstoneTools.simpleAngle.mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDrag', cornerstoneTools.simpleAngle.mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.simpleAngle.mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.simpleAngle.mouseDownActivateCallback); - cornerstone.updateImage(element); - }); - }); - } - - function addNewMeasurementTouch(touchEventData) { - var measurementData = createNewMeasurement(touchEventData); - var element = touchEventData.element; - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(element, toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.simpleAngleTouch.touchMoveCallback); - $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.simpleAngleTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.simpleAngleTouch.touchStartCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.simpleAngleTouch.tapCallback); - cornerstone.updateImage(element); - - cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.middle, function() { - if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.simpleAngleTouch.touchMoveCallback); - $(element).on('CornerstoneToolsTouchStart', cornerstoneTools.simpleAngleTouch.touchStartCallback); - $(element).on('CornerstoneToolsTouchStartActive', cornerstoneTools.simpleAngleTouch.touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', cornerstoneTools.simpleAngleTouch.tapCallback); - cornerstone.updateImage(element); - return; - } - - cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() { - if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - cornerstone.updateImage(element); - } - - $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.simpleAngleTouch.touchMoveCallback); - $(element).on('CornerstoneToolsTouchStart', cornerstoneTools.simpleAngleTouch.touchStartCallback); - $(element).on('CornerstoneToolsTouchStartActive', cornerstoneTools.simpleAngleTouch.touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', cornerstoneTools.simpleAngleTouch.tapCallback); - }); - }); - } - - cornerstoneTools.simpleAngle = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - addNewMeasurement: addNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - - cornerstoneTools.simpleAngleTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - addNewMeasurement: addNewMeasurementTouch, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/simpleAngle.js - -// Begin Source: src/imageTools/textMarker.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var toolType = 'textMarker'; - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - var config = cornerstoneTools.textMarker.getConfiguration(); - - if (!config.current) { - return; - } - - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - text: config.current, - handles: { - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true, - hasBoundingBox: true - } - } - }; - - // Create a rectangle representing the image - var imageRect = { - left: 0, - top: 0, - width: mouseEventData.image.width, - height: mouseEventData.image.height - }; - - // Check if the current handle is outside the image, - // If it is, prevent the handle creation - if (!cornerstoneMath.point.insideRect(measurementData.handles.end, imageRect)) { - return; - } - - // Update the current marker for the next marker - var currentIndex = config.markers.indexOf(config.current); - if (config.ascending) { - currentIndex += 1; - if (currentIndex >= config.markers.length) { - if (!config.loop) { - currentIndex = -1; - } else { - currentIndex -= config.markers.length; - } - } - } else { - currentIndex -= 1; - if (currentIndex < 0) { - if (!config.loop) { - currentIndex = -1; - } else { - currentIndex += config.markers.length; - } - } - } - - config.current = config.markers[currentIndex]; - - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - ///////// BEGIN IMAGE RENDERING /////// - function pointNearTool(element, data, coords) { - if (!data.handles.end.boundingBox) { - return; - } - - var distanceToPoint = cornerstoneMath.rect.distanceToPoint(data.handles.end.boundingBox, coords); - var insideBoundingBox = cornerstoneTools.pointInsideBoundingBox(data.handles.end, coords); - return (distanceToPoint < 10) || insideBoundingBox; - } - - function onImageRendered(e, eventData) { - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData) { - return; - } - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var config = cornerstoneTools.textMarker.getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - var data = toolData.data[i]; - - var color = cornerstoneTools.toolColors.getToolColor(); - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } - - context.save(); - - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - // Draw text - context.fillStyle = color; - var measureText = context.measureText(data.text); - data.textWidth = measureText.width + 10; - - var textCoords = cornerstone.pixelToCanvas(eventData.element, data.handles.end); - - var options = { - centering: { - x: true, - y: true - } - }; - - var boundingBox = cornerstoneTools.drawTextBox(context, data.text, textCoords.x, textCoords.y - 10, color, options); - data.handles.end.boundingBox = boundingBox; - - context.restore(); - } - } - - function doubleClickCallback(e, eventData) { - var element = eventData.element; - var data; - - function doneChangingTextCallback(data, updatedText, deleteTool) { - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - } else { - data.text = updatedText; - } - - data.active = false; - cornerstone.updateImage(element); - - var mouseButtonData = { - mouseButtonMask: e.data.mouseButtonMask - }; - - $(element).on('CornerstoneToolsMouseMove', mouseButtonData, cornerstoneTools.textMarker.mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', mouseButtonData, cornerstoneTools.textMarker.mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', mouseButtonData, cornerstoneTools.textMarker.mouseDownActivateCallback); - $(element).on('CornerstoneToolsMouseDoubleClick', mouseButtonData, cornerstoneTools.textMarker.mouseDoubleClickCallback); - } - - if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - return; - } - - var config = cornerstoneTools.textMarker.getConfiguration(); - - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); - - // now check to see if there is a handle we can move - if (!toolData) { - return; - } - - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords)) { - data.active = true; - cornerstone.updateImage(element); - - $(element).off('CornerstoneToolsMouseMove', cornerstoneTools.textMarker.mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', cornerstoneTools.textMarker.mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.textMarker.mouseDownActivateCallback); - $(element).off('CornerstoneToolsMouseDoubleClick', cornerstoneTools.textMarker.mouseDoubleClickCallback); - // Allow relabelling via a callback - config.changeTextCallback(data, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - function touchPressCallback(e, eventData) { - var element = eventData.element; - var data; - - function doneChangingTextCallback(data, updatedText, deleteTool) { - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - } else { - data.text = updatedText; - } - - data.active = false; - cornerstone.updateImage(element); - - $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.textMarkerTouch.touchMoveCallback); - $(element).on('CornerstoneToolsTouchStartActive', cornerstoneTools.textMarkerTouch.touchDownActivateCallback); - $(element).on('CornerstoneToolsTouchStart', cornerstoneTools.textMarkerTouch.touchStartCallback); - $(element).on('CornerstoneToolsTap', cornerstoneTools.textMarkerTouch.tapCallback); - $(element).on('CornerstoneToolsTouchPress', cornerstoneTools.textMarkerTouch.pressCallback); - } - - var config = cornerstoneTools.textMarker.getConfiguration(); - - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); - - // now check to see if there is a handle we can move - if (!toolData) { - return false; - } - - if (eventData.handlePressed) { - eventData.handlePressed.active = true; - cornerstone.updateImage(element); - - $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.textMarkerTouch.touchMoveCallback); - $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.textMarkerTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.textMarkerTouch.touchStartCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.textMarkerTouch.tapCallback); - $(element).off('CornerstoneToolsTouchPress', cornerstoneTools.textMarkerTouch.pressCallback); - - // Allow relabelling via a callback - config.changeTextCallback(eventData.handlePressed, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords)) { - data.active = true; - cornerstone.updateImage(element); - - $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.textMarkerTouch.touchMoveCallback); - $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.textMarkerTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.textMarkerTouch.touchStartCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.textMarkerTouch.tapCallback); - $(element).off('CornerstoneToolsTouchPress', cornerstoneTools.textMarkerTouch.pressCallback); - // Allow relabelling via a callback - config.changeTextCallback(data, eventData, doneChangingTextCallback); - - e.stopImmediatePropagation(); - return false; - } - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - cornerstoneTools.textMarker = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType, - mouseDoubleClickCallback: doubleClickCallback - }); - - cornerstoneTools.textMarkerTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType, - pressCallback: touchPressCallback - }); - - ///////// END IMAGE RENDERING /////// - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/textMarker.js - -// Begin Source: src/imageTools/wwwc.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseDrag', mouseDragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - $(eventData.element).on('CornerstoneToolsMouseDrag', mouseDragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).on('CornerstoneToolsMouseClick', mouseUpCallback); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - } - - function defaultStrategy(eventData) { - // here we normalize the ww/wc adjustments so the same number of on screen pixels - // adjusts the same percentage of the dynamic range of the image. This is needed to - // provide consistency for the ww/wc tool regardless of the dynamic range (e.g. an 8 bit - // image will feel the same as a 16 bit image would) - var maxVOI = eventData.image.maxPixelValue * eventData.image.slope + eventData.image.intercept; - var minVOI = eventData.image.minPixelValue * eventData.image.slope + eventData.image.intercept; - var imageDynamicRange = maxVOI - minVOI; - var multiplier = imageDynamicRange / 1024; - - var deltaX = eventData.deltaPoints.page.x * multiplier; - var deltaY = eventData.deltaPoints.page.y * multiplier; - - eventData.viewport.voi.windowWidth += (deltaX); - eventData.viewport.voi.windowCenter += (deltaY); - } - - function mouseDragCallback(e, eventData) { - cornerstoneTools.wwwc.strategy(eventData); - cornerstone.setViewport(eventData.element, eventData.viewport); - return false; // false = cases jquery to preventDefault() and stopPropagation() this event - } - - function touchDragCallback(e, eventData) { - e.stopImmediatePropagation(); // Prevent CornerstoneToolsTouchStartActive from killing any press events - var dragData = eventData; - - var maxVOI = dragData.image.maxPixelValue * dragData.image.slope + dragData.image.intercept; - var minVOI = dragData.image.minPixelValue * dragData.image.slope + dragData.image.intercept; - var imageDynamicRange = maxVOI - minVOI; - var multiplier = imageDynamicRange / 1024; - var deltaX = dragData.deltaPoints.page.x * multiplier; - var deltaY = dragData.deltaPoints.page.y * multiplier; - - var config = cornerstoneTools.wwwc.getConfiguration(); - if (config.orientation) { - if (config.orientation === 0) { - dragData.viewport.voi.windowWidth += (deltaX); - dragData.viewport.voi.windowCenter += (deltaY); - } else { - dragData.viewport.voi.windowWidth += (deltaY); - dragData.viewport.voi.windowCenter += (deltaX); - } - } else { - dragData.viewport.voi.windowWidth += (deltaX); - dragData.viewport.voi.windowCenter += (deltaY); - } - - cornerstone.setViewport(dragData.element, dragData.viewport); - } - - cornerstoneTools.wwwc = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); - cornerstoneTools.wwwc.strategies = { - default: defaultStrategy - }; - cornerstoneTools.wwwc.strategy = defaultStrategy; - cornerstoneTools.wwwcTouchDrag = cornerstoneTools.touchDragTool(touchDragCallback); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/wwwc.js - -// Begin Source: src/imageTools/wwwcRegion.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - var toolType = 'wwwcRegion'; - - var configuration = { - minWindowWidth: 10 - }; - - var currentMouseButtonMask; - - /** Calculates the minimum, maximum, and mean value in the given pixel array */ - function calculateMinMaxMean(storedPixelLuminanceData, globalMin, globalMax) { - var numPixels = storedPixelLuminanceData.length; - - if (numPixels < 2) { - return { - min: globalMin, - max: globalMax, - mean: (globalMin + globalMax) / 2 - }; - } - - var min = globalMax; - var max = globalMin; - var sum = 0; - - for (var index = 0; index < numPixels; index++) { - var spv = storedPixelLuminanceData[index]; - min = Math.min(min, spv); - max = Math.max(max, spv); - sum += spv; - } - - return { - min: min, - max: max, - mean: sum / numPixels - }; - } - - /* Erases the toolData and rebinds the handlers when the image changes */ - function newImageCallback(e, eventData) { - //console.log('newImageCallback: ' + e.type); - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (toolData && toolData.data) { - toolData.data = []; - } - - $(eventData.element).off('CornerstoneToolsMouseMove', dragCallback); - $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); - - $(eventData.element).off('CornerstoneToolsMouseUp', dragEndCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', dragEndCallback); - - var mouseData = { - mouseButtonMask: currentMouseButtonMask - }; - - $(eventData.element).on('CornerstoneToolsMouseDown', mouseData, mouseDownCallback); - } - - /* Applies the windowing procedure when the mouse drag ends */ - function dragEndCallback(e, eventData) { - //console.log('dragEndCallback: ' + e.type); - $(eventData.element).off('CornerstoneToolsMouseMove', dragCallback); - $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); - - $(eventData.element).off('CornerstoneToolsMouseUp', dragEndCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', dragEndCallback); - - var mouseData = { - mouseButtonMask: currentMouseButtonMask - }; - - $(eventData.element).on('CornerstoneToolsMouseDown', mouseData, mouseDownCallback); - - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData || !toolData.data || !toolData.data.length) { - return; - } - - // Update the endpoint as the mouse/touch is dragged - toolData.data[0].endPoint = { - x: eventData.currentPoints.image.x, - y: eventData.currentPoints.image.y - }; - - applyWWWCRegion(eventData); - } - - /** Calculates the minimum and maximum value in the given pixel array */ - function applyWWWCRegion(eventData) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData || !toolData.data || !toolData.data.length) { - return; - } - - var startPoint = toolData.data[0].startPoint; - var endPoint = toolData.data[0].endPoint; - - // Get the rectangular region defined by the handles - var width = Math.abs(startPoint.x - endPoint.x); - var height = Math.abs(startPoint.y - endPoint.y); - - var left = Math.min(startPoint.x, endPoint.x); - var top = Math.min(startPoint.y, endPoint.y); - - // Bound the rectangle so we don't get undefined pixels - left = Math.max(left, 0); - left = Math.min(left, eventData.image.width); - top = Math.max(top, 0); - top = Math.min(top, eventData.image.height); - width = Math.floor(Math.min(width, Math.abs(eventData.image.width - left))); - height = Math.floor(Math.min(height, Math.abs(eventData.image.height - top))); - - // Get the pixel data in the rectangular region - var pixelLuminanceData = cornerstoneTools.getLuminance(eventData.element, left, top, width, height); - - // Calculate the minimum and maximum pixel values - var minMaxMean = calculateMinMaxMean(pixelLuminanceData, eventData.image.minPixelValue, eventData.image.maxPixelValue); - - // Adjust the viewport window width and center based on the calculated values - var config = cornerstoneTools.wwwcRegion.getConfiguration(); - var viewport = cornerstone.getViewport(eventData.element); - if (config.minWindowWidth === undefined) { - config.minWindowWidth = 10; - } - - viewport.voi.windowWidth = Math.max(Math.abs(minMaxMean.max - minMaxMean.min), config.minWindowWidth); - viewport.voi.windowCenter = minMaxMean.mean; - cornerstone.setViewport(eventData.element, viewport); - - // Clear the toolData - toolData.data = []; - - cornerstone.updateImage(eventData.element); - } - - function whichMovement(e, eventData) { - //console.log('whichMovement: ' + e.type); - var element = eventData.element; - - $(element).off('CornerstoneToolsMouseMove'); - $(element).off('CornerstoneToolsMouseDrag'); - - $(element).on('CornerstoneToolsMouseMove', dragCallback); - $(element).on('CornerstoneToolsMouseDrag', dragCallback); - - $(element).on('CornerstoneToolsMouseClick', dragEndCallback); - if (e.type === 'CornerstoneToolsMouseDrag') { - $(element).on('CornerstoneToolsMouseUp', dragEndCallback); - } - } - - /** Records the start point and attaches the drag event handler */ - function mouseDownCallback(e, eventData) { - //console.log('mouseDownCallback: ' + e.type); - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - $(eventData.element).on('CornerstoneToolsMouseDrag', eventData, whichMovement); - $(eventData.element).on('CornerstoneToolsMouseMove', eventData, whichMovement); - - $(eventData.element).off('CornerstoneToolsMouseDown', mouseDownCallback); - recordStartPoint(eventData); - return false; - } - } - - /** Records the start point of the click or touch */ - function recordStartPoint(eventData) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (toolData && toolData.data) { - toolData.data = []; - } - - var measurementData = { - startPoint: { - x: eventData.currentPoints.image.x, - y: eventData.currentPoints.image.y - } - }; - - cornerstoneTools.addToolState(eventData.element, toolType, measurementData); - } - - /** Draws the rectangular region while the touch or mouse event drag occurs */ - function dragCallback(e, eventData) { - //console.log('dragCallback: ' + e.type); - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData || !toolData.data || !toolData.data.length) { - return; - } - - // Update the endpoint as the mouse/touch is dragged - var endPoint = { - x: eventData.currentPoints.image.x, - y: eventData.currentPoints.image.y - }; - - toolData.data[0].endPoint = endPoint; - cornerstone.updateImage(eventData.element); - } - - function onImageRendered(e, eventData) { - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData || !toolData.data || !toolData.data.length) { - return; - } - - var startPoint = toolData.data[0].startPoint; - var endPoint = toolData.data[0].endPoint; - - if (!startPoint || !endPoint) { - return; - } - - // Get the current element's canvas - var canvas = $(eventData.element).find('canvas').get(0); - var context = canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - // Set to the active tool color - var color = cornerstoneTools.toolColors.getActiveColor(); - - // Calculate the rectangle parameters - var startPointCanvas = cornerstone.pixelToCanvas(eventData.element, startPoint); - var endPointCanvas = cornerstone.pixelToCanvas(eventData.element, endPoint); - - var left = Math.min(startPointCanvas.x, endPointCanvas.x); - var top = Math.min(startPointCanvas.y, endPointCanvas.y); - var width = Math.abs(startPointCanvas.x - endPointCanvas.x); - var height = Math.abs(startPointCanvas.y - endPointCanvas.y); - - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var config = cornerstoneTools.wwwcRegion.getConfiguration(); - - // Draw the rectangle - context.save(); - - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.rect(left, top, width, height); - context.stroke(); - - context.restore(); - } - - // --- Mouse tool enable / disable --- /// - function disable(element) { - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - - $(element).off('CornerstoneToolsMouseUp', dragEndCallback); - $(element).off('CornerstoneToolsMouseClick', dragEndCallback); - - $(element).off('CornerstoneToolsMouseDrag', dragCallback); - $(element).off('CornerstoneToolsMouseMove', dragCallback); - - $(element).off('CornerstoneImageRendered', onImageRendered); - $(element).off('CornerstoneNewImage', newImageCallback); - - cornerstone.updateImage(element); - } - - function activate(element, mouseButtonMask) { - var eventData = { - mouseButtonMask: mouseButtonMask, - }; - - currentMouseButtonMask = mouseButtonMask; - - var toolData = cornerstoneTools.getToolState(element, toolType); - if (!toolData) { - var data = []; - cornerstoneTools.addToolState(element, toolType, data); - } - - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - - $(element).off('CornerstoneToolsMouseUp', dragEndCallback); - $(element).off('CornerstoneToolsMouseClick', dragEndCallback); - - $(element).off('CornerstoneToolsMouseDrag', dragCallback); - $(element).off('CornerstoneToolsMouseMove', dragCallback); - - $(element).off('CornerstoneImageRendered', onImageRendered); - $(element).off('CornerstoneNewImage', newImageCallback); - - $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); - $(element).on('CornerstoneImageRendered', onImageRendered); - - // If the displayed image changes after the user has started clicking, we should - // cancel the handlers and prepare for another click - $(element).on('CornerstoneNewImage', newImageCallback); - - cornerstone.updateImage(element); - } - - // --- Touch tool enable / disable --- // - function disableTouchDrag(element) { - $(element).off('CornerstoneToolsTouchDrag', dragCallback); - $(element).off('CornerstoneToolsTouchStart', recordStartPoint); - $(element).off('CornerstoneToolsDragEnd', applyWWWCRegion); - $(element).off('CornerstoneImageRendered', onImageRendered); - } - - function activateTouchDrag(element) { - var toolData = cornerstoneTools.getToolState(element, toolType); - if (!toolData) { - var data = []; - cornerstoneTools.addToolState(element, toolType, data); - } - - $(element).off('CornerstoneToolsTouchDrag', dragCallback); - $(element).off('CornerstoneToolsTouchStart', recordStartPoint); - $(element).off('CornerstoneToolsDragEnd', applyWWWCRegion); - $(element).off('CornerstoneImageRendered', onImageRendered); - - $(element).on('CornerstoneToolsTouchDrag', dragCallback); - $(element).on('CornerstoneToolsTouchStart', recordStartPoint); - $(element).on('CornerstoneToolsDragEnd', applyWWWCRegion); - $(element).on('CornerstoneImageRendered', onImageRendered); - } - - function getConfiguration() { - return configuration; - } - - function setConfiguration(config) { - configuration = config; - } - - // module exports - cornerstoneTools.wwwcRegion = { - activate: activate, - deactivate: disable, - disable: disable, - setConfiguration: setConfiguration, - getConfiguration: getConfiguration - }; - - cornerstoneTools.wwwcRegionTouch = { - activate: activateTouchDrag, - deactivate: disableTouchDrag, - disable: disableTouchDrag - }; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/imageTools/wwwcRegion.js - -// Begin Source: src/imageTools/zoom.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var startPoints; - - function changeViewportScale(viewport, ticks) { - var config = cornerstoneTools.zoom.getConfiguration(); - var pow = 1.7; - - var oldFactor = Math.log(viewport.scale) / Math.log(pow); - var factor = oldFactor + ticks; - - var scale = Math.pow(pow, factor); - if (config.maxScale && scale > config.maxScale) { - viewport.scale = config.maxScale; - } else if (config.minScale && scale < config.minScale) { - viewport.scale = config.minScale; - } else { - viewport.scale = scale; - } - - return viewport; - } - - function boundPosition(position, width, height) { - position.x = Math.max(position.x, 0); - position.y = Math.max(position.y, 0); - position.x = Math.min(position.x, width); - position.y = Math.min(position.y, height); - return position; - } - - function correctShift(shift, viewport) { - // Apply Flips - if (viewport.hflip) { - shift.x *= -1; - } - - if (viewport.vflip) { - shift.y *= -1; - } - - // Apply rotations - if (viewport.rotation !== 0) { - var angle = viewport.rotation * Math.PI / 180; - - var cosA = Math.cos(angle); - var sinA = Math.sin(angle); - - var newX = shift.x * cosA - shift.y * sinA; - var newY = shift.x * sinA + shift.y * cosA; - - shift.x = newX; - shift.y = newY; - } - - return shift; - } - - function defaultStrategy(eventData, ticks) { - var element = eventData.element; - - // Calculate the new scale factor based on how far the mouse has changed - var viewport = changeViewportScale(eventData.viewport, ticks); - cornerstone.setViewport(element, viewport); - - // Now that the scale has been updated, determine the offset we need to apply to the center so we can - // keep the original start location in the same position - var newCoords = cornerstone.pageToPixel(element, eventData.startPoints.page.x, eventData.startPoints.page.y); - - // The shift we will use is the difference between the original image coordinates of the point we've selected - // and the image coordinates of the same point on the page after the viewport scaling above has been performed - // This shift is in image coordinates, and is designed to keep the target location fixed on the page. - var shift = { - x: eventData.startPoints.image.x - newCoords.x, - y: eventData.startPoints.image.y - newCoords.y - }; - - // Correct the required shift using the viewport rotation and flip parameters - shift = correctShift(shift, viewport); - - // Apply the shift to the Viewport's translation setting - viewport.translation.x -= shift.x; - viewport.translation.y -= shift.y; - - // Update the Viewport with the new translation value - cornerstone.setViewport(element, viewport); - } - - function translateStrategy(eventData, ticks) { - var element = eventData.element; - var image = eventData.image; - var config = cornerstoneTools.zoom.getConfiguration(); - - // Calculate the new scale factor based on how far the mouse has changed - // Note that in this case we don't need to update the viewport after the initial - // zoom step since we aren't don't intend to keep the target position static on - // the page - var viewport = changeViewportScale(eventData.viewport, ticks); - - // Define the default shift to take place during this zoom step - var shift = { - x: 0, - y: 0 - }; - - // Define the parameters for the translate strategy - var translateSpeed = 8; - var outwardsMinScaleToTranslate = 3; - var minTranslation = 0.01; - - if (ticks < 0) { - // Zoom outwards from the image center - if (viewport.scale < outwardsMinScaleToTranslate) { - // If the current translation is smaller than the minimum desired translation, - // set the translation to zero - if (Math.abs(viewport.translation.x) < minTranslation) { - viewport.translation.x = 0; - } else { - shift.x = viewport.translation.x / translateSpeed; - } - - // If the current translation is smaller than the minimum desired translation, - // set the translation to zero - if (Math.abs(viewport.translation.y) < minTranslation) { - viewport.translation.y = 0; - } else { - shift.y = viewport.translation.y / translateSpeed; - } - } - } else { - // Zoom inwards to the current image point - - // Identify the coordinates of the point the user is trying to zoom into - // If we are not allowed to zoom outside the image, bound the user-selected position to - // a point inside the image - if (config && config.preventZoomOutsideImage) { - startPoints.image = boundPosition(startPoints.image, image.width, image.height); - } - - // Calculate the translation value that would place the desired image point in the center - // of the viewport - var desiredTranslation = { - x: image.width / 2 - startPoints.image.x, - y: image.height / 2 - startPoints.image.y - }; - - // Correct the target location using the viewport rotation and flip parameters - desiredTranslation = correctShift(desiredTranslation, viewport); - - // Calculate the difference between the current viewport translation value and the - // final desired translation values - var distanceToDesired = { - x: viewport.translation.x - desiredTranslation.x, - y: viewport.translation.y - desiredTranslation.y - }; - - // If the current translation is smaller than the minimum desired translation, - // stop translating in the x-direction - if (Math.abs(distanceToDesired.x) < minTranslation) { - viewport.translation.x = desiredTranslation.x; - } else { - // Otherwise, shift the viewport by one step - shift.x = distanceToDesired.x / translateSpeed; - } - - // If the current translation is smaller than the minimum desired translation, - // stop translating in the y-direction - if (Math.abs(distanceToDesired.y) < minTranslation) { - viewport.translation.y = desiredTranslation.y; - } else { - // Otherwise, shift the viewport by one step - shift.y = distanceToDesired.y / translateSpeed; - } - } - - // Apply the shift to the Viewport's translation setting - viewport.translation.x -= shift.x; - viewport.translation.y -= shift.y; - - // Update the Viewport with the new translation value - cornerstone.setViewport(element, viewport); - } - - function zoomToCenterStrategy(eventData, ticks) { - var element = eventData.element; - - // Calculate the new scale factor based on how far the mouse has changed - var viewport = changeViewportScale(eventData.viewport, ticks); - cornerstone.setViewport(element, viewport); - } - - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - startPoints = eventData.startPoints; // Used for translateStrategy - $(eventData.element).on('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).on('CornerstoneToolsMouseClick', mouseUpCallback); - return false; // false = cases jquery to preventDefault() and stopPropagation() this event - } - } - - function dragCallback(e, eventData) { - if (!eventData.deltaPoints.page.y) { - return false; - } - - var ticks = eventData.deltaPoints.page.y / 100; - cornerstoneTools.zoom.strategy(eventData, ticks); - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - function mouseWheelCallback(e, eventData) { - var ticks = -eventData.direction / 4; - - // Allow inversion of the mouse wheel scroll via a configuration option - var config = cornerstoneTools.zoom.getConfiguration(); - if (config && config.invert) { - ticks *= -1; - } - - var viewport = changeViewportScale(eventData.viewport, ticks); - cornerstone.setViewport(eventData.element, viewport); - } - - function touchPinchCallback(e, eventData) { - var config = cornerstoneTools.zoom.getConfiguration(); - var viewport = eventData.viewport; - var element = eventData.element; - - // Change the scale based on the pinch gesture's scale change - viewport.scale += eventData.scaleChange * viewport.scale; - if (config.maxScale && viewport.scale > config.maxScale) { - viewport.scale = config.maxScale; - } else if (config.minScale && viewport.scale < config.minScale) { - viewport.scale = config.minScale; - } - - cornerstone.setViewport(element, viewport); - - // Now that the scale has been updated, determine the offset we need to apply to the center so we can - // keep the original start location in the same position - var newCoords = cornerstone.pageToPixel(element, eventData.startPoints.page.x, eventData.startPoints.page.y); - var shift = { - x: eventData.startPoints.image.x - newCoords.x, - y: eventData.startPoints.image.y - newCoords.y - }; - - shift = correctShift(shift, viewport); - viewport.translation.x -= shift.x; - viewport.translation.y -= shift.y; - cornerstone.setViewport(element, viewport); - } - - cornerstoneTools.zoom = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); - cornerstoneTools.zoom.strategies = { - default: defaultStrategy, - translate: translateStrategy, - zoomToCenter: zoomToCenterStrategy - }; - cornerstoneTools.zoom.strategy = defaultStrategy; - - cornerstoneTools.zoomWheel = cornerstoneTools.mouseWheelTool(mouseWheelCallback); - cornerstoneTools.zoomTouchPinch = cornerstoneTools.touchPinchTool(touchPinchCallback); - cornerstoneTools.zoomTouchDrag = cornerstoneTools.touchDragTool(dragCallback); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/imageTools/zoom.js - -// Begin Source: src/inputSources/keyboardInput.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var mouseX, mouseY; - - function keyPress(e) { - var element = e.currentTarget; - - var keyPressData = { - event: window.event || e, // old IE support - element: element, - viewport: cornerstone.getViewport(element), - image: cornerstone.getEnabledElement(element).image, - currentPoints: { - page: { - x: mouseX, - y: mouseY - }, - image: cornerstone.pageToPixel(element, mouseX, mouseY), - }, - keyCode: e.keyCode, - which: e.which - }; - - keyPressData.currentPoints.canvas = cornerstone.pixelToCanvas(element, keyPressData.currentPoints.image); - - var keyPressEvents = { - keydown: 'CornerstoneToolsKeyDown', - keypress: 'CornerstoneToolsKeyPress', - keyup: 'CornerstoneToolsKeyUp', - - }; - - $(element).trigger(keyPressEvents[e.type], keyPressData); - } - - function mouseMove(e) { - mouseX = e.pageX || e.originalEvent.pageX; - mouseY = e.pageY || e.originalEvent.pageY; - } - - var keyboardEvent = 'keydown keypress keyup'; - - function enable(element) { - // Prevent handlers from being attached multiple times - disable(element); - - $(element).on(keyboardEvent, keyPress); - $(element).on('mousemove', mouseMove); - } - - function disable(element) { - $(element).off(keyboardEvent, keyPress); - $(element).off('mousemove', mouseMove); - } - - // module exports - cornerstoneTools.keyboardInput = { - enable: enable, - disable: disable - }; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/inputSources/keyboardInput.js - -// Begin Source: src/inputSources/preventGhostClick.js -(function(cornerstoneTools) { - - 'use strict'; - - // Functions to prevent ghost clicks following a touch - // All credit to @kosich - // https://gist.github.com/kosich/23188dd86633b6c2efb7 - - var antiGhostDelay = 2000, - pointerType = { - mouse: 0, - touch: 1 - }, - lastInteractionType, - lastInteractionTime; - - function handleTap(type, e) { - var now = Date.now(); - if (type !== lastInteractionType) { - if (now - lastInteractionTime <= antiGhostDelay) { - e.preventDefault(); - e.stopPropagation(); - e.stopImmediatePropagation(); - return false; - } - - lastInteractionType = type; - } - - lastInteractionTime = now; - } - - // Cacheing the function references - // Necessary because a new function reference is created after .bind() is called - // http://stackoverflow.com/questions/11565471/removing-event-listener-which-was-added-with-bind - var handleTapMouse = handleTap.bind(null, pointerType.mouse); - var handleTapTouch = handleTap.bind(null, pointerType.touch); - - function attachEvents(element, eventList, interactionType) { - var tapHandler = interactionType ? handleTapMouse : handleTapTouch; - eventList.forEach(function(eventName) { - element.addEventListener(eventName, tapHandler, true); - }); - } - - function removeEvents(element, eventList, interactionType) { - var tapHandler = interactionType ? handleTapMouse : handleTapTouch; - eventList.forEach(function(eventName) { - element.removeEventListener(eventName, tapHandler, true); - }); - } - - var mouseEvents = [ 'mousedown', 'mouseup' ]; - var touchEvents = [ 'touchstart', 'touchend' ]; - - function disable(element) { - removeEvents(element, mouseEvents, pointerType.mouse); - removeEvents(element, touchEvents, pointerType.touch); - } - - function enable(element) { - disable(element); - attachEvents(element, mouseEvents, pointerType.mouse); - attachEvents(element, touchEvents, pointerType.touch); - } - - cornerstoneTools.preventGhostClick = { - enable: enable, - disable: disable - }; - -})(cornerstoneTools); - -// End Source; src/inputSources/preventGhostClick.js - -// Begin Source: src/manipulators/anyHandlesOutsideImage.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function anyHandlesOutsideImage(renderData, handles) { - var image = renderData.image; - var imageRect = { - left: 0, - top: 0, - width: image.width, - height: image.height - }; - - var handleOutsideImage = false; - - Object.keys(handles).forEach(function(name) { - var handle = handles[name]; - if (handle.allowedOutsideImage === true) { - return; - } - - if (cornerstoneMath.point.insideRect(handle, imageRect) === false) { - handleOutsideImage = true; - } - }); - - return handleOutsideImage; - } - - // module/private exports - cornerstoneTools.anyHandlesOutsideImage = anyHandlesOutsideImage; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/manipulators/anyHandlesOutsideImage.js - -// Begin Source: src/manipulators/drawHandles.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - var handleRadius = 6; - - function drawHandles(context, renderData, handles, color, options) { - context.strokeStyle = color; - - Object.keys(handles).forEach(function(name) { - var handle = handles[name]; - if (handle.drawnIndependently === true) { - return; - } - - if (options && options.drawHandlesIfActive === true && !handle.active) { - return; - } - - context.beginPath(); - - if (handle.active) { - context.lineWidth = cornerstoneTools.toolStyle.getActiveWidth(); - } else { - context.lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - } - - var handleCanvasCoords = cornerstone.pixelToCanvas(renderData.element, handle); - context.arc(handleCanvasCoords.x, handleCanvasCoords.y, handleRadius, 0, 2 * Math.PI); - - if (options && options.fill) { - context.fillStyle = options.fill; - context.fill(); - } - - context.stroke(); - }); - } - - // module/private exports - cornerstoneTools.drawHandles = drawHandles; - -})(cornerstone, cornerstoneTools); - -// End Source; src/manipulators/drawHandles.js - -// Begin Source: src/manipulators/getHandleNearImagePoint.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function getHandleNearImagePoint(element, handles, coords, distanceThreshold) { - var nearbyHandle; - - if (!handles) { - return; - } - - Object.keys(handles).forEach(function(name) { - var handle = handles[name]; - if (handle.hasOwnProperty('pointNearHandle')) { - if (handle.pointNearHandle(element, handle, coords)) { - nearbyHandle = handle; - return; - } - } else if (handle.hasBoundingBox === true) { - if (cornerstoneTools.pointInsideBoundingBox(handle, coords)) { - nearbyHandle = handle; - return; - } - } else { - var handleCanvas = cornerstone.pixelToCanvas(element, handle); - var distance = cornerstoneMath.point.distance(handleCanvas, coords); - if (distance <= distanceThreshold) { - nearbyHandle = handle; - return; - } - } - }); - - return nearbyHandle; - } - - // module exports - cornerstoneTools.getHandleNearImagePoint = getHandleNearImagePoint; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/manipulators/getHandleNearImagePoint.js - -// Begin Source: src/manipulators/handleActivator.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function getActiveHandle(handles) { - var activeHandle; - - Object.keys(handles).forEach(function(name) { - var handle = handles[name]; - if (handle.active === true) { - activeHandle = handle; - return; - } - }); - - return activeHandle; - } - - function handleActivator(element, handles, canvasPoint, distanceThreshold) { - if (!distanceThreshold) { - distanceThreshold = 6; - } - - var activeHandle = getActiveHandle(handles); - var nearbyHandle = cornerstoneTools.getHandleNearImagePoint(element, handles, canvasPoint, distanceThreshold); - if (activeHandle !== nearbyHandle) { - if (nearbyHandle !== undefined) { - nearbyHandle.active = true; - } - - if (activeHandle !== undefined) { - activeHandle.active = false; - } - - return true; - } - - return false; - } - - // module/private exports - cornerstoneTools.handleActivator = handleActivator; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/manipulators/handleActivator.js - -// Begin Source: src/manipulators/moveAllHandles.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function moveAllHandles(mouseEventData, data, toolData, toolType, options, doneMovingCallback) { - var element = mouseEventData.element; - - function mouseDragCallback(e, eventData) { - data.active = true; - - Object.keys(data.handles).forEach(function(name) { - var handle = data.handles[name]; - if (handle.movesIndependently === true) { - return; - } - - handle.x += eventData.deltaPoints.image.x; - handle.y += eventData.deltaPoints.image.y; - - if (options.preventHandleOutsideImage === true) { - handle.x = Math.max(handle.x, 0); - handle.x = Math.min(handle.x, eventData.image.width); - - handle.y = Math.max(handle.y, 0); - handle.y = Math.min(handle.y, eventData.image.height); - } - }); - - cornerstone.updateImage(element); - - var eventType = 'CornerstoneToolsMeasurementModified'; - var modifiedEventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, modifiedEventData); - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - $(element).on('CornerstoneToolsMouseDrag', mouseDragCallback); - - function mouseUpCallback(e, eventData) { - data.invalidated = true; - - $(element).off('CornerstoneToolsMouseDrag', mouseDragCallback); - $(element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).off('CornerstoneToolsMouseClick', mouseUpCallback); - - // If any handle is outside the image, delete the tool data - if (options.deleteIfHandleOutsideImage === true && - cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) { - cornerstoneTools.removeToolState(element, toolType, data); - } - - cornerstone.updateImage(element); - - if (typeof doneMovingCallback === 'function') { - doneMovingCallback(); - } - } - - $(element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).on('CornerstoneToolsMouseClick', mouseUpCallback); - return true; - } - - // module/private exports - cornerstoneTools.moveAllHandles = moveAllHandles; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/manipulators/moveAllHandles.js - -// Begin Source: src/manipulators/moveHandle.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function moveHandle(mouseEventData, toolType, data, handle, doneMovingCallback, preventHandleOutsideImage) { - var element = mouseEventData.element; - var distanceFromTool = { - x: handle.x - mouseEventData.currentPoints.image.x, - y: handle.y - mouseEventData.currentPoints.image.y - }; - - function mouseDragCallback(e, eventData) { - if (handle.hasMoved === false) { - handle.hasMoved = true; - } - - handle.active = true; - handle.x = eventData.currentPoints.image.x + distanceFromTool.x; - handle.y = eventData.currentPoints.image.y + distanceFromTool.y; - - if (preventHandleOutsideImage) { - handle.x = Math.max(handle.x, 0); - handle.x = Math.min(handle.x, eventData.image.width); - - handle.y = Math.max(handle.y, 0); - handle.y = Math.min(handle.y, eventData.image.height); - } - - cornerstone.updateImage(element); - - var eventType = 'CornerstoneToolsMeasurementModified'; - var modifiedEventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, modifiedEventData); - } - - $(element).on('CornerstoneToolsMouseDrag', mouseDragCallback); - - function mouseUpCallback() { - handle.active = false; - $(element).off('CornerstoneToolsMouseDrag', mouseDragCallback); - $(element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).off('CornerstoneToolsMouseClick', mouseUpCallback); - cornerstone.updateImage(element); - - if (typeof doneMovingCallback === 'function') { - doneMovingCallback(); - } - } - - $(element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).on('CornerstoneToolsMouseClick', mouseUpCallback); - } - - // module/private exports - cornerstoneTools.moveHandle = moveHandle; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/manipulators/moveHandle.js - -// Begin Source: src/manipulators/moveNewHandle.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function moveNewHandle(mouseEventData, toolType, data, handle, doneMovingCallback, preventHandleOutsideImage) { - var element = mouseEventData.element; - - function moveCallback(e, eventData) { - handle.active = true; - handle.x = eventData.currentPoints.image.x; - handle.y = eventData.currentPoints.image.y; - - if (preventHandleOutsideImage) { - handle.x = Math.max(handle.x, 0); - handle.x = Math.min(handle.x, eventData.image.width); - - handle.y = Math.max(handle.y, 0); - handle.y = Math.min(handle.y, eventData.image.height); - } - - cornerstone.updateImage(element); - - var eventType = 'CornerstoneToolsMeasurementModified'; - var modifiedEventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, modifiedEventData); - } - - function whichMovement(e) { - $(element).off('CornerstoneToolsMouseMove'); - $(element).off('CornerstoneToolsMouseDrag'); - - $(element).on('CornerstoneToolsMouseMove', moveCallback); - $(element).on('CornerstoneToolsMouseDrag', moveCallback); - - $(element).on('CornerstoneToolsMouseClick', moveEndCallback); - if (e.type === 'CornerstoneToolsMouseDrag') { - $(element).on('CornerstoneToolsMouseUp', moveEndCallback); - } - } - - function measurementRemovedCallback(e, eventData) { - if (eventData.measurementData === data) { - moveEndCallback(); - } - } - - function toolDeactivatedCallback(e, eventData) { - if (eventData.toolType === toolType) { - $(element).off('CornerstoneToolsMouseMove', moveCallback); - $(element).off('CornerstoneToolsMouseDrag', moveCallback); - $(element).off('CornerstoneToolsMouseClick', moveEndCallback); - $(element).off('CornerstoneToolsMouseUp', moveEndCallback); - $(element).off('CornerstoneToolsMeasurementRemoved', measurementRemovedCallback); - $(element).off('CornerstoneToolsToolDeactivated', toolDeactivatedCallback); - - handle.active = false; - cornerstone.updateImage(element); - } - } - - $(element).on('CornerstoneToolsMouseDrag', whichMovement); - $(element).on('CornerstoneToolsMouseMove', whichMovement); - $(element).on('CornerstoneToolsMeasurementRemoved', measurementRemovedCallback); - $(element).on('CornerstoneToolsToolDeactivated', toolDeactivatedCallback); - - function moveEndCallback() { - $(element).off('CornerstoneToolsMouseMove', moveCallback); - $(element).off('CornerstoneToolsMouseDrag', moveCallback); - $(element).off('CornerstoneToolsMouseClick', moveEndCallback); - $(element).off('CornerstoneToolsMouseUp', moveEndCallback); - $(element).off('CornerstoneToolsMeasurementRemoved', measurementRemovedCallback); - $(element).off('CornerstoneToolsToolDeactivated', toolDeactivatedCallback); - - handle.active = false; - cornerstone.updateImage(element); - - if (typeof doneMovingCallback === 'function') { - doneMovingCallback(); - } - } - } - - // module/private exports - cornerstoneTools.moveNewHandle = moveNewHandle; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/manipulators/moveNewHandle.js - -// Begin Source: src/manipulators/moveNewHandleTouch.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function moveNewHandleTouch(eventData, toolType, data, handle, doneMovingCallback, preventHandleOutsideImage) { - //console.log('moveNewHandleTouch'); - var element = eventData.element; - var imageCoords = cornerstone.pageToPixel(element, eventData.currentPoints.page.x, eventData.currentPoints.page.y + 50); - var distanceFromTouch = { - x: handle.x - imageCoords.x, - y: handle.y - imageCoords.y - }; - - handle.active = true; - data.active = true; - - function moveCallback(e, eventData) { - handle.x = eventData.currentPoints.image.x + distanceFromTouch.x; - handle.y = eventData.currentPoints.image.y + distanceFromTouch.y; - - if (preventHandleOutsideImage) { - handle.x = Math.max(handle.x, 0); - handle.x = Math.min(handle.x, eventData.image.width); - - handle.y = Math.max(handle.y, 0); - handle.y = Math.min(handle.y, eventData.image.height); - } - - cornerstone.updateImage(element); - - var eventType = 'CornerstoneToolsMeasurementModified'; - var modifiedEventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, modifiedEventData); - } - - function moveEndCallback(e, eventData) { - $(element).off('CornerstoneToolsTouchDrag', moveCallback); - $(element).off('CornerstoneToolsTouchPinch', moveEndCallback); - $(element).off('CornerstoneToolsTouchEnd', moveEndCallback); - $(element).off('CornerstoneToolsTap', moveEndCallback); - $(element).off('CornerstoneToolsTouchStart', stopImmediatePropagation); - $(element).off('CornerstoneToolsToolDeactivated', toolDeactivatedCallback); - - if (e.type === 'CornerstoneToolsTouchPinch' || e.type === 'CornerstoneToolsTouchPress') { - handle.active = false; - cornerstone.updateImage(element); - doneMovingCallback(); - return; - } - - handle.active = false; - data.active = false; - handle.x = eventData.currentPoints.image.x + distanceFromTouch.x; - handle.y = eventData.currentPoints.image.y + distanceFromTouch.y; - - if (preventHandleOutsideImage) { - handle.x = Math.max(handle.x, 0); - handle.x = Math.min(handle.x, eventData.image.width); - - handle.y = Math.max(handle.y, 0); - handle.y = Math.min(handle.y, eventData.image.height); - } - - cornerstone.updateImage(element); - - if (typeof doneMovingCallback === 'function') { - doneMovingCallback(); - } - } - - function stopImmediatePropagation(e) { - // Stop the CornerstoneToolsTouchStart event from - // become a CornerstoneToolsTouchStartActive event when - // moveNewHandleTouch ends - e.stopImmediatePropagation(); - return false; - } - - $(element).on('CornerstoneToolsTouchDrag', moveCallback); - $(element).on('CornerstoneToolsTouchPinch', moveEndCallback); - $(element).on('CornerstoneToolsTouchEnd', moveEndCallback); - $(element).on('CornerstoneToolsTap', moveEndCallback); - $(element).on('CornerstoneToolsTouchStart', stopImmediatePropagation); - - function toolDeactivatedCallback() { - $(element).off('CornerstoneToolsTouchDrag', moveCallback); - $(element).off('CornerstoneToolsTouchPinch', moveEndCallback); - $(element).off('CornerstoneToolsTouchEnd', moveEndCallback); - $(element).off('CornerstoneToolsTap', moveEndCallback); - $(element).off('CornerstoneToolsTouchStart', stopImmediatePropagation); - $(element).off('CornerstoneToolsToolDeactivated', toolDeactivatedCallback); - - handle.active = false; - data.active = false; - handle.x = eventData.currentPoints.image.x + distanceFromTouch.x; - handle.y = eventData.currentPoints.image.y + distanceFromTouch.y; - - if (preventHandleOutsideImage) { - handle.x = Math.max(handle.x, 0); - handle.x = Math.min(handle.x, eventData.image.width); - - handle.y = Math.max(handle.y, 0); - handle.y = Math.min(handle.y, eventData.image.height); - } - - cornerstone.updateImage(element); - } - - $(element).on('CornerstoneToolsToolDeactivated', toolDeactivatedCallback); - } - - // module/private exports - cornerstoneTools.moveNewHandleTouch = moveNewHandleTouch; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/manipulators/moveNewHandleTouch.js - -// Begin Source: src/manipulators/touchMoveAllHandles.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function touchMoveAllHandles(touchEventData, data, toolData, toolType, deleteIfHandleOutsideImage, doneMovingCallback) { - //console.log('touchMoveAllHandles'); - var element = touchEventData.element; - - function touchDragCallback(e, eventData) { - //console.log('touchMoveAllHandles touchDragCallback'); - data.active = true; - - Object.keys(data.handles).forEach(function(name) { - var handle = data.handles[name]; - if (handle.movesIndependently === true) { - return; - } - - handle.x += eventData.deltaPoints.image.x; - handle.y += eventData.deltaPoints.image.y; - }); - cornerstone.updateImage(element); - - var eventType = 'CornerstoneToolsMeasurementModified'; - var modifiedEventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, modifiedEventData); - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - $(element).on('CornerstoneToolsTouchDrag', touchDragCallback); - - function touchEndCallback(e, eventData) { - //console.log('touchMoveAllHandles touchEndCallback: ' + e.type); - data.active = false; - data.invalidated = false; - - $(element).off('CornerstoneToolsTouchDrag', touchDragCallback); - $(element).off('CornerstoneToolsTouchPinch', touchEndCallback); - $(element).off('CornerstoneToolsTouchPress', touchEndCallback); - $(element).off('CornerstoneToolsTouchEnd', touchEndCallback); - $(element).off('CornerstoneToolsDragEnd', touchEndCallback); - $(element).off('CornerstoneToolsTap', touchEndCallback); - - // If any handle is outside the image, delete the tool data - if (deleteIfHandleOutsideImage === true && - cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) { - cornerstoneTools.toolState.removeToolState(element, toolType, data); - } - - cornerstone.updateImage(element); - - if (typeof doneMovingCallback === 'function') { - doneMovingCallback(e, eventData); - } - } - - $(element).on('CornerstoneToolsTouchPinch', touchEndCallback); - $(element).on('CornerstoneToolsTouchPress', touchEndCallback); - $(element).on('CornerstoneToolsTouchEnd', touchEndCallback); - $(element).on('CornerstoneToolsDragEnd', touchEndCallback); - $(element).on('CornerstoneToolsTap', touchEndCallback); - return true; - } - - // module/private exports - cornerstoneTools.touchMoveAllHandles = touchMoveAllHandles; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/manipulators/touchMoveAllHandles.js - -// Begin Source: src/manipulators/touchMoveHandle.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - /* - * define the runAnimation boolean as an object - * so that it can be modified by reference - */ - var runAnimation = { - value: false - }; - - var touchEndEvents = [ 'CornerstoneToolsTouchEnd', - 'CornerstoneToolsDragEnd', - 'CornerstoneToolsTouchPinch', - 'CornerstoneToolsTouchPress', - 'CornerstoneToolsTap' - ].join(' '); - - function animate(lastTime, handle, runAnimation, enabledElement, targetLocation) { - // See http://www.html5canvastutorials.com/advanced/html5-canvas-start-and-stop-an-animation/ - if (!runAnimation.value) { - return; - } - - // update - var time = (new Date()).getTime(); - //var timeDiff = time - lastTime; - - // pixels / second - var distanceRemaining = Math.abs(handle.y - targetLocation.y); - var linearDistEachFrame = distanceRemaining / 10; - - console.log('distanceRemaining: ' + distanceRemaining); - if (distanceRemaining < 1) { - handle.y = targetLocation.y; - runAnimation.value = false; - return; - } - - if (handle.y > targetLocation.y) { - handle.y -= linearDistEachFrame; - } else if (handle.y < targetLocation.y) { - handle.y += linearDistEachFrame; - } - - // Update the image - cornerstone.updateImage(enabledElement.element); - - // Request a new frame - cornerstone.requestAnimationFrame(function() { - animate(time, handle, runAnimation, enabledElement, targetLocation); - }); - } - - function touchMoveHandle(touchEventData, toolType, data, handle, doneMovingCallback) { - //console.log('touchMoveHandle'); - runAnimation.value = true; - - var element = touchEventData.element; - var enabledElement = cornerstone.getEnabledElement(element); - - var time = (new Date()).getTime(); - - // Average pixel width of index finger is 45-57 pixels - // https://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/ - var fingerDistance = -57; - - var aboveFinger = { - x: touchEventData.currentPoints.page.x, - y: touchEventData.currentPoints.page.y + fingerDistance - }; - - var targetLocation = cornerstone.pageToPixel(element, aboveFinger.x, aboveFinger.y); - - function touchDragCallback(e, eventData) { - //console.log('touchMoveHandle touchDragCallback: ' + e.type); - runAnimation.value = false; - - if (handle.hasMoved === false) { - handle.hasMoved = true; - } - - handle.active = true; - - var currentPoints = eventData.currentPoints; - var aboveFinger = { - x: currentPoints.page.x, - y: currentPoints.page.y + fingerDistance - }; - - targetLocation = cornerstone.pageToPixel(element, aboveFinger.x, aboveFinger.y); - handle.x = targetLocation.x; - handle.y = targetLocation.y; - - cornerstone.updateImage(element); - - var eventType = 'CornerstoneToolsMeasurementModified'; - var modifiedEventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, modifiedEventData); - } - - $(element).on('CornerstoneToolsTouchDrag', touchDragCallback); - - function touchEndCallback(e, eventData) { - //console.log('touchMoveHandle touchEndCallback: ' + e.type); - runAnimation.value = false; - - handle.active = false; - $(element).off('CornerstoneToolsTouchDrag', touchDragCallback); - $(element).off(touchEndEvents, touchEndCallback); - - cornerstone.updateImage(element); - - if (e.type === 'CornerstoneToolsTouchPress') { - eventData.handlePressed = data; - - handle.x = touchEventData.currentPoints.image.x; - handle.y = touchEventData.currentPoints.image.y; - } - - if (typeof doneMovingCallback === 'function') { - doneMovingCallback(e, eventData); - } - } - - $(element).on(touchEndEvents, touchEndCallback); - - animate(time, handle, runAnimation, enabledElement, targetLocation); - } - - // module/private exports - cornerstoneTools.touchMoveHandle = touchMoveHandle; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/manipulators/touchMoveHandle.js - -// Begin Source: src/measurementManager/lineSample.js -(function($, cornerstoneTools) { - - 'use strict'; - - // This object manages a collection of measurements - function LineSampleMeasurement() { - - var that = this; - that.samples = []; - - // adds an element as both a source and a target - this.set = function(samples) { - that.samples = samples; - // fire event - $(that).trigger('CornerstoneLineSampleUpdated'); - }; - } - - // module/private exports - cornerstoneTools.LineSampleMeasurement = LineSampleMeasurement; - -})($, cornerstoneTools); - -// End Source; src/measurementManager/lineSample.js - -// Begin Source: src/measurementManager/measurementManager.js -(function($, cornerstoneTools) { - - 'use strict'; - - // This object manages a collection of measurements - function MeasurementManager() { - - var that = this; - that.measurements = []; - - // adds an element as both a source and a target - this.add = function(measurement) { - var index = that.measurements.push(measurement); - // fire event - var eventDetail = { - index: index, - measurement: measurement - }; - $(that).trigger('CornerstoneMeasurementAdded', eventDetail); - }; - - this.remove = function(index) { - var measurement = that.measurements[index]; - that.measurements.splice(index, 1); - // fire event - var eventDetail = { - index: index, - measurement: measurement - }; - $(that).trigger('CornerstoneMeasurementRemoved', eventDetail); - }; - - } - - // module/private exports - cornerstoneTools.MeasurementManager = new MeasurementManager(); - -})($, cornerstoneTools); - -// End Source; src/measurementManager/measurementManager.js - -// Begin Source: src/metaData.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // module/private exports - cornerstoneTools.metaData = cornerstone.metaData; -})($, cornerstone, cornerstoneTools); - -// End Source; src/metaData.js - -// Begin Source: src/orientation/getOrientationString.js -(function(cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function getOrientationString(vector) { - // Thanks to David Clunie - // https://sites.google.com/site/dicomnotes/ - - var orientation = '', - orientationX = vector.x < 0 ? 'R' : 'L', - orientationY = vector.y < 0 ? 'A' : 'P', - orientationZ = vector.z < 0 ? 'F' : 'H'; - - // Should probably make this a function vector3.abs - var abs = new cornerstoneMath.Vector3(Math.abs(vector.x), Math.abs(vector.y), Math.abs(vector.z)); - - for (var i = 0; i < 3; i++) { - if (abs.x > 0.0001 && abs.x > abs.y && abs.x > abs.z) { - orientation += orientationX; - abs.x = 0; - } else if (abs.y > 0.0001 && abs.y > abs.x && abs.y > abs.z) { - orientation += orientationY; - abs.y = 0; - } else if (abs.z > 0.0001 && abs.z > abs.x && abs.z > abs.y) { - orientation += orientationZ; - abs.z = 0; - } else { - break; - } - } - - return orientation; - } - - // module/private exports - cornerstoneTools.orientation.getOrientationString = getOrientationString; - -})(cornerstoneMath, cornerstoneTools); - -// End Source; src/orientation/getOrientationString.js - -// Begin Source: src/orientation/invertOrientationString.js -(function(cornerstoneTools) { - - 'use strict'; - - function invertOrientationString(string) { - var inverted = string.replace('H', 'f'); - inverted = inverted.replace('F', 'h'); - inverted = inverted.replace('R', 'l'); - inverted = inverted.replace('L', 'r'); - inverted = inverted.replace('A', 'p'); - inverted = inverted.replace('P', 'a'); - inverted = inverted.toUpperCase(); - return inverted; - } - - // module/private exports - cornerstoneTools.orientation.invertOrientationString = invertOrientationString; - -})(cornerstoneTools); - -// End Source; src/orientation/invertOrientationString.js - -// Begin Source: src/referenceLines/calculateReferenceLine.js -(function(cornerstoneTools) { - - 'use strict'; - - // calculates a reference line between two planes by projecting the top left hand corner and bottom right hand corner - // of the reference image onto the target image. Ideally we would calculate the intersection between the planes but - // that requires a bit more math and this works fine for most cases - function calculateReferenceLine(targetImagePlane, referenceImagePlane) { - var points = cornerstoneTools.planePlaneIntersection(targetImagePlane, referenceImagePlane); - if (!points) { - return; - } - - return { - start: cornerstoneTools.projectPatientPointToImagePlane(points.start, targetImagePlane), - end: cornerstoneTools.projectPatientPointToImagePlane(points.end, targetImagePlane) - }; - } - - // module/private exports - cornerstoneTools.referenceLines.calculateReferenceLine = calculateReferenceLine; - -})(cornerstoneTools); - -// End Source; src/referenceLines/calculateReferenceLine.js - -// Begin Source: src/referenceLines/referenceLinesTool.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var toolType = 'referenceLines'; - - function onImageRendered(e, eventData) { - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (toolData === undefined) { - return; - } - - // Get the enabled elements associated with this synchronization context and draw them - var syncContext = toolData.data[0].synchronizationContext; - var enabledElements = syncContext.getSourceElements(); - - var renderer = toolData.data[0].renderer; - - // Create the canvas context and reset it to the pixel coordinate system - var context = eventData.canvasContext.canvas.getContext('2d'); - cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, context); - - // Iterate over each referenced element - $.each(enabledElements, function(index, referenceEnabledElement) { - - // don't draw ourselves - if (referenceEnabledElement === e.currentTarget) { - return; - } - - // render it - renderer(context, eventData, e.currentTarget, referenceEnabledElement); - }); - } - - // enables the reference line tool for a given element. Note that a custom renderer - // can be provided if you want different rendering (e.g. all reference lines, first/last/active, etc) - function enable(element, synchronizationContext, renderer) { - renderer = renderer || cornerstoneTools.referenceLines.renderActiveReferenceLine; - - cornerstoneTools.addToolState(element, toolType, { - synchronizationContext: synchronizationContext, - renderer: renderer - }); - $(element).on('CornerstoneImageRendered', onImageRendered); - cornerstone.updateImage(element); - } - - // disables the reference line tool for the given element - function disable(element) { - $(element).off('CornerstoneImageRendered', onImageRendered); - cornerstone.updateImage(element); - } - - // module/private exports - cornerstoneTools.referenceLines.tool = { - enable: enable, - disable: disable - - }; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/referenceLines/referenceLinesTool.js - -// Begin Source: src/referenceLines/renderActiveReferenceLine.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // renders the active reference line - function renderActiveReferenceLine(context, eventData, targetElement, referenceElement) { - var targetImage = cornerstone.getEnabledElement(targetElement).image; - var referenceImage = cornerstone.getEnabledElement(referenceElement).image; - - // make sure the images are actually loaded for the target and reference - if (!targetImage || !referenceImage) { - return; - } - - var targetImagePlane = cornerstoneTools.metaData.get('imagePlane', targetImage.imageId); - var referenceImagePlane = cornerstoneTools.metaData.get('imagePlane', referenceImage.imageId); - - // Make sure the target and reference actually have image plane metadata - if (!targetImagePlane || - !referenceImagePlane || - !targetImagePlane.rowCosines || - !targetImagePlane.columnCosines || - !targetImagePlane.imagePositionPatient || - !referenceImagePlane.rowCosines || - !referenceImagePlane.columnCosines || - !referenceImagePlane.imagePositionPatient) { - return; - } - - // the image planes must be in the same frame of reference - if (targetImagePlane.frameOfReferenceUID !== referenceImagePlane.frameOfReferenceUID) { - return; - } - - // the image plane normals must be > 30 degrees apart - var targetNormal = targetImagePlane.rowCosines.clone().cross(targetImagePlane.columnCosines); - var referenceNormal = referenceImagePlane.rowCosines.clone().cross(referenceImagePlane.columnCosines); - var angleInRadians = targetNormal.angleTo(referenceNormal); - - angleInRadians = Math.abs(angleInRadians); - if (angleInRadians < 0.5) { // 0.5 radians = ~30 degrees - return; - } - - var referenceLine = cornerstoneTools.referenceLines.calculateReferenceLine(targetImagePlane, referenceImagePlane); - if (!referenceLine) { - return; - } - - var refLineStartCanvas = cornerstone.pixelToCanvas(eventData.element, referenceLine.start); - var refLineEndCanvas = cornerstone.pixelToCanvas(eventData.element, referenceLine.end); - - var color = cornerstoneTools.toolColors.getActiveColor(); - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - - // draw the referenceLines - context.setTransform(1, 0, 0, 1, 0, 0); - - context.save(); - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.moveTo(refLineStartCanvas.x, refLineStartCanvas.y); - context.lineTo(refLineEndCanvas.x, refLineEndCanvas.y); - context.stroke(); - context.restore(); - } - - // module/private exports - cornerstoneTools.referenceLines.renderActiveReferenceLine = renderActiveReferenceLine; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/referenceLines/renderActiveReferenceLine.js - -// Begin Source: src/requestPool/requestPoolManager.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - var requestPool = { - interaction: [], - thumbnail: [], - prefetch: [] - }; - - var numRequests = { - interaction: 0, - thumbnail: 0, - prefetch: 0 - }; - - var maxNumRequests = { - interaction: 6, - thumbnail: 6, - prefetch: 5 - }; - - var lastElementInteracted; - var awake = false; - var grabDelay = 20; - - function requestPoolManager() { - - function addRequest(element, imageId, type, preventCache, doneCallback, failCallback) { - if (!requestPool.hasOwnProperty(type)) { - throw 'Request type must be one of interaction, thumbnail, or prefetch'; - } - - if (!element || !imageId) { - return; - } - - // Describe the request - var requestDetails = { - type: type, - imageId: imageId, - preventCache: preventCache, - doneCallback: doneCallback, - failCallback: failCallback - }; - - // If this imageId is in the cache, resolve it immediately - var imagePromise = cornerstone.imageCache.getImagePromise(imageId); - if (imagePromise) { - imagePromise.then(function(image) { - doneCallback(image); - }, function(error) { - failCallback(error); - }); - return; - } - - // Add it to the end of the stack - requestPool[type].push(requestDetails); - - // Store the last element interacted with, - // So we know which images to prefetch - // - // ---- Not used for now ---- - if (type === 'interaction') { - lastElementInteracted = element; - } - } - - function clearRequestStack(type) { - //console.log('clearRequestStack'); - if (!requestPool.hasOwnProperty(type)) { - throw 'Request type must be one of interaction, thumbnail, or prefetch'; - } - - requestPool[type] = []; - } - - function startAgain() { - if (!awake) { - return; - } - - setTimeout(function() { - startGrabbing(); - }, grabDelay); - } - - function sendRequest(requestDetails) { - // Increment the number of current requests of this type - var type = requestDetails.type; - numRequests[type]++; - - awake = true; - var imageId = requestDetails.imageId; - var doneCallback = requestDetails.doneCallback; - var failCallback = requestDetails.failCallback; - - // Check if we already have this image promise in the cache - var imagePromise = cornerstone.imageCache.getImagePromise(imageId); - if (imagePromise) { - // If we do, remove from list (when resolved, as we could have - // pending prefetch requests) and stop processing this iteration - imagePromise.then(function(image) { - numRequests[type]--; - // console.log(numRequests); - - doneCallback(image); - startAgain(); - }, function(error) { - numRequests[type]--; - // console.log(numRequests); - failCallback(error); - startAgain(); - }); - return; - } - - function requestTypeToLoadPriority(requestDetails) { - if (requestDetails.type === 'prefetch') { - return -5; - } else if (requestDetails.type === 'interactive') { - return 0; - } else if (requestDetails.type === 'thumbnail') { - return 5; - } - } - - var priority = requestTypeToLoadPriority(requestDetails); - - var loader; - if (requestDetails.preventCache === true) { - loader = cornerstone.loadImage(imageId, { - priority: priority, - type: requestDetails.type - }); - } else { - loader = cornerstone.loadAndCacheImage(imageId, { - priority: priority, - type: requestDetails.type - }); - } - - // Load and cache the image - loader.then(function(image) { - numRequests[type]--; - // console.log(numRequests); - doneCallback(image); - startAgain(); - }, function(error) { - numRequests[type]--; - // console.log(numRequests); - failCallback(error); - startAgain(); - }); - } - - function startGrabbing() { - // Begin by grabbing X images - var maxSimultaneousRequests = cornerstoneTools.getMaxSimultaneousRequests(); - - maxNumRequests = { - interaction: Math.max(maxSimultaneousRequests, 1), - thumbnail: Math.max(maxSimultaneousRequests - 2, 1), - prefetch: Math.max(maxSimultaneousRequests - 1, 1) - }; - - var currentRequests = numRequests.interaction + - numRequests.thumbnail + - numRequests.prefetch; - var requestsToSend = maxSimultaneousRequests - currentRequests; - for (var i = 0; i < requestsToSend; i++) { - var requestDetails = getNextRequest(); - if (requestDetails) { - sendRequest(requestDetails); - } - } - - //console.log("startGrabbing"); - //console.log(requestPool); - } - - function getNextRequest() { - if (requestPool.interaction.length && numRequests.interaction < maxNumRequests.interaction) { - return requestPool.interaction.shift(); - } - - if (requestPool.thumbnail.length && numRequests.thumbnail < maxNumRequests.thumbnail) { - return requestPool.thumbnail.shift(); - } - - if (requestPool.prefetch.length && numRequests.prefetch < maxNumRequests.prefetch) { - return requestPool.prefetch.shift(); - } - - if (!requestPool.interaction.length && - !requestPool.thumbnail.length && - !requestPool.prefetch.length) { - awake = false; - } - - return false; - } - - function getRequestPool() { - return requestPool; - } - - var requestManager = { - addRequest: addRequest, - clearRequestStack: clearRequestStack, - startGrabbing: startGrabbing, - getRequestPool: getRequestPool - }; - - return requestManager; - } - - // module/private exports - cornerstoneTools.requestPoolManager = requestPoolManager(); - -})(cornerstone, cornerstoneTools); - -// End Source; src/requestPool/requestPoolManager.js - -// Begin Source: src/stackTools/playClip.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var toolType = 'playClip'; - - /** - * [private] Turns a Frame Time Vector (0018,1065) array into a normalized array of timeouts. Each element - * ... of the resulting array represents the amount of time each frame will remain on the screen. - * @param {Array} vector A Frame Time Vector (0018,1065) as specified in section C.7.6.5.1.2 of DICOM standard. - * @param {Number} speed A speed factor which will be applied to each element of the resulting array. - * @return {Array} An array with timeouts for each animation frame. - */ - function getPlayClipTimeouts(vector, speed) { - - var i, - sample, - delay, - sum = 0, - limit = vector.length, - timeouts = []; - - // initialize time varying to false - timeouts.isTimeVarying = false; - - if (typeof speed !== 'number' || speed <= 0) { - speed = 1; - } - - // first element of a frame time vector must be discarded - for (i = 1; i < limit; i++) { - delay = (+vector[i] / speed) | 0; // integral part only - timeouts.push(delay); - if (i === 1) { // use first item as a sample for comparison - sample = delay; - } else if (delay !== sample) { - timeouts.isTimeVarying = true; - } - - sum += delay; - } - - if (timeouts.length > 0) { - if (timeouts.isTimeVarying) { - // if it's a time varying vector, make the last item an average... - delay = (sum / timeouts.length) | 0; - } else { - delay = timeouts[0]; - } - - timeouts.push(delay); - } - - return timeouts; - - } - - /** - * [private] Performs the heavy lifting of stopping an ongoing animation. - * @param {Object} playClipData The data from playClip that needs to be stopped. - * @return void - */ - function stopClipWithData(playClipData) { - var id = playClipData.intervalId; - if (typeof id !== 'undefined') { - playClipData.intervalId = undefined; - if (playClipData.usingFrameTimeVector) { - clearTimeout(id); - } else { - clearInterval(id); - } - } - } - - /** - * [private] Trigger playClip tool stop event. - * @param element - * @return void - */ - function triggerStopEvent(element) { - var event, - eventDetail = { - element: element - }; - event = $.Event('CornerstoneToolsClipStopped', eventDetail); - $(element).trigger(event, eventDetail); - } - - /** - * Starts playing a clip or adjusts the frame rate of an already playing clip. framesPerSecond is - * optional and defaults to 30 if not specified. A negative framesPerSecond will play the clip in reverse. - * The element must be a stack of images - * @param element - * @param framesPerSecond - */ - function playClip(element, framesPerSecond) { - - // hoisting of context variables - var stackToolData, - stackData, - playClipToolData, - playClipData, - playClipTimeouts, - playClipAction; - - if (element === undefined) { - throw 'playClip: element must not be undefined'; - } - - stackToolData = cornerstoneTools.getToolState(element, 'stack'); - if (!stackToolData || !stackToolData.data || !stackToolData.data.length) { - return; - } - - stackData = stackToolData.data[0]; - - playClipToolData = cornerstoneTools.getToolState(element, toolType); - if (!playClipToolData || !playClipToolData.data || !playClipToolData.data.length) { - playClipData = { - intervalId: undefined, - framesPerSecond: 30, - lastFrameTimeStamp: undefined, - frameRate: 0, - frameTimeVector: undefined, - ignoreFrameTimeVector: false, - usingFrameTimeVector: false, - speed: 1, - reverse: false, - loop: true - }; - cornerstoneTools.addToolState(element, toolType, playClipData); - } else { - playClipData = playClipToolData.data[0]; - // make sure the specified clip is not running before any property update - stopClipWithData(playClipData); - } - - // If a framesPerSecond is specified and is valid, update the playClipData now - if (framesPerSecond < 0 || framesPerSecond > 0) { - playClipData.framesPerSecond = +framesPerSecond; - playClipData.reverse = playClipData.framesPerSecond < 0; - // if framesPerSecond is given, frameTimeVector will be ignored... - playClipData.ignoreFrameTimeVector = true; - } - - // determine if frame time vector should be used instead of a fixed frame rate... - if ( - playClipData.ignoreFrameTimeVector !== true && - playClipData.frameTimeVector && - playClipData.frameTimeVector.length === stackData.imageIds.length - ) { - playClipTimeouts = getPlayClipTimeouts(playClipData.frameTimeVector, playClipData.speed); - } - - // this function encapsulates the frame rendering logic... - playClipAction = function playClipAction() { - - // hoisting of context variables - var loader, - viewport, - startLoadingHandler, - endLoadingHandler, - errorLoadingHandler, - newImageIdIndex = stackData.currentImageIdIndex, - imageCount = stackData.imageIds.length; - - if (playClipData.reverse) { - newImageIdIndex--; - } else { - newImageIdIndex++; - } - - if (!playClipData.loop && (newImageIdIndex < 0 || newImageIdIndex >= imageCount)) { - stopClipWithData(playClipData); - triggerStopEvent(element); - return; - } - - // loop around if we go outside the stack - if (newImageIdIndex >= imageCount) { - newImageIdIndex = 0; - } - - if (newImageIdIndex < 0) { - newImageIdIndex = imageCount - 1; - } - - if (newImageIdIndex !== stackData.currentImageIdIndex) { - - startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); - endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler(); - errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - - if (startLoadingHandler) { - startLoadingHandler(element); - } - - viewport = cornerstone.getViewport(element); - - if (stackData.preventCache === true) { - loader = cornerstone.loadImage(stackData.imageIds[newImageIdIndex]); - } else { - loader = cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]); - } - - loader.then(function(image) { - stackData.currentImageIdIndex = newImageIdIndex; - cornerstone.displayImage(element, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(element, image); - } - }, function(error) { - var imageId = stackData.imageIds[newImageIdIndex]; - if (errorLoadingHandler) { - errorLoadingHandler(element, imageId, error); - } - }); - - } - - }; - - // if playClipTimeouts array is available, not empty and its elements are NOT uniform ... - // ... (at least one timeout is different from the others), use alternate setTimeout implementation - if (playClipTimeouts && playClipTimeouts.length > 0 && playClipTimeouts.isTimeVarying) { - playClipData.usingFrameTimeVector = true; - playClipData.intervalId = setTimeout(function playClipTimeoutHandler() { - playClipData.intervalId = setTimeout(playClipTimeoutHandler, playClipTimeouts[stackData.currentImageIdIndex]); - playClipAction(); - }, 0); - } else { - // ... otherwise user setInterval implementation which is much more efficient. - playClipData.usingFrameTimeVector = false; - playClipData.intervalId = setInterval(playClipAction, 1000 / Math.abs(playClipData.framesPerSecond)); - } - - } - - /** - * Stops an already playing clip. - * * @param element - */ - function stopClip(element) { - - var playClipToolData = cornerstoneTools.getToolState(element, toolType); - - if (!playClipToolData || !playClipToolData.data || !playClipToolData.data.length) { - return; - } - - stopClipWithData(playClipToolData.data[0]); - - } - - // module/private exports - cornerstoneTools.playClip = playClip; - cornerstoneTools.stopClip = stopClip; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/stackTools/playClip.js - -// Begin Source: src/stackTools/scrollIndicator.js -/* -Display scroll progress bar across bottom of image. - */ -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var scrollBarHeight = 6; - - var configuration = { - backgroundColor: 'rgb(19, 63, 141)', - fillColor: 'white', - orientation: 'horizontal' - }; - - function onImageRendered(e, eventData){ - var element = eventData.element; - var width = eventData.enabledElement.canvas.width; - var height = eventData.enabledElement.canvas.height; - - if (!width || !height) { - return false; - } - - var context = eventData.enabledElement.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - context.save(); - - var config = cornerstoneTools.scrollIndicator.getConfiguration(); - - // draw indicator background - context.fillStyle = config.backgroundColor; - if (config.orientation === 'horizontal') { - context.fillRect(0, height - scrollBarHeight, width, scrollBarHeight); - } else { - context.fillRect(0, 0, scrollBarHeight, height); - } - - // get current image index - var stackData = cornerstoneTools.getToolState(element, 'stack'); - if (!stackData || !stackData.data || !stackData.data.length) { - return; - } - - var imageIds = stackData.data[0].imageIds; - var currentImageIdIndex = stackData.data[0].currentImageIdIndex; - - // draw current image cursor - var cursorWidth = width / imageIds.length; - var cursorHeight = height / imageIds.length; - var xPosition = cursorWidth * currentImageIdIndex; - var yPosition = cursorHeight * currentImageIdIndex; - - context.fillStyle = config.fillColor; - if (config.orientation === 'horizontal') { - context.fillRect(xPosition, height - scrollBarHeight, cursorWidth, scrollBarHeight); - } else { - context.fillRect(0, yPosition, scrollBarHeight, cursorHeight); - } - - context.restore(); - } - - cornerstoneTools.scrollIndicator = cornerstoneTools.displayTool(onImageRendered); - cornerstoneTools.scrollIndicator.setConfiguration(configuration); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/stackTools/scrollIndicator.js - -// Begin Source: src/stackTools/stackPrefetch.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var toolType = 'stackPrefetch'; - var requestType = 'prefetch'; - - var configuration = {}; - - var resetPrefetchTimeout, - resetPrefetchDelay = 300; - - function sortNumber(a, b) { - // http://stackoverflow.com/questions/1063007/how-to-sort-an-array-of-integers-correctly - return a - b; - } - - function range(lowEnd, highEnd) { - // Javascript version of Python's range function - // http://stackoverflow.com/questions/3895478/does-javascript-have-a-method-like-range-to-generate-an-array-based-on-suppl - lowEnd = Math.round(lowEnd) || 0; - highEnd = Math.round(highEnd) || 0; - - var arr = [], - c = highEnd - lowEnd + 1; - - if (c <= 0) { - return arr; - } - - while ( c-- ) { - arr[c] = highEnd--; - } - - return arr; - } - - var max = function(arr) { - return Math.max.apply(null, arr); - }; - - var min = function(arr) { - return Math.min.apply(null, arr); - }; - - function nearestIndex(arr, x) { - // Return index of nearest values in array - // http://stackoverflow.com/questions/25854212/return-index-of-nearest-values-in-an-array - var l = [], - h = []; - - arr.forEach(function(v) { - if (v < x) { - l.push(v); - } else if (v > x) { - h.push(v); - } - }); - - return { - low: arr.indexOf(max(l)), - high: arr.indexOf(min(h)) - }; - } - - function prefetch(element) { - // Check to make sure stack data exists - var stackData = cornerstoneTools.getToolState(element, 'stack'); - if (!stackData || !stackData.data || !stackData.data.length) { - return; - } - - var stack = stackData.data[0]; - - // Get the stackPrefetch tool data - var stackPrefetchData = cornerstoneTools.getToolState(element, toolType); - if (!stackPrefetchData) { - return; - } - - var stackPrefetch = stackPrefetchData.data[0]; - - // If all the requests are complete, disable the stackPrefetch tool - if (!stackPrefetch || !stackPrefetch.indicesToRequest || !stackPrefetch.indicesToRequest.length) { - stackPrefetch.enabled = false; - } - - // Make sure the tool is still enabled - if (stackPrefetch.enabled === false) { - return; - } - - // Remove an imageIdIndex from the list of indices to request - // This fires when the individual image loading deferred is resolved - function removeFromList(imageIdIndex) { - var index = stackPrefetch.indicesToRequest.indexOf(imageIdIndex); - if (index > -1) { // don't remove last element if imageIdIndex not found - stackPrefetch.indicesToRequest.splice(index, 1); - } - } - - // Remove all already cached images from the - // indicesToRequest array - stackPrefetchData.data[0].indicesToRequest.sort(sortNumber); - var indicesToRequestCopy = stackPrefetch.indicesToRequest.slice(); - - indicesToRequestCopy.forEach(function(imageIdIndex) { - var imageId = stack.imageIds[imageIdIndex]; - - if (!imageId) { - return; - } - - var imagePromise = cornerstone.imageCache.getImagePromise(imageId); - if (imagePromise && imagePromise.state() === 'resolved'){ - removeFromList(imageIdIndex); - } - }); - - // Stop here if there are no images left to request - // After those in the cache have been removed - if (!stackPrefetch.indicesToRequest.length) { - return; - } - - // Clear the requestPool of prefetch requests - var requestPoolManager = cornerstoneTools.requestPoolManager; - requestPoolManager.clearRequestStack(requestType); - - // Identify the nearest imageIdIndex to the currentImageIdIndex - var nearest = nearestIndex(stackPrefetch.indicesToRequest, stack.currentImageIdIndex); - - var imageId, - nextImageIdIndex, - preventCache = false; - - function doneCallback(image) { - //console.log('prefetch done: ' + image.imageId); - var imageIdIndex = stack.imageIds.indexOf(image.imageId); - removeFromList(imageIdIndex); - } - - // Retrieve the errorLoadingHandler if one exists - var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - - function failCallback(error) { - console.log('prefetch errored: ' + error); - if (errorLoadingHandler) { - errorLoadingHandler(element, imageId, error, 'stackPrefetch'); - } - } - - // Prefetch images around the current image (before and after) - var lowerIndex = nearest.low; - var higherIndex = nearest.high; - while (lowerIndex > 0 || higherIndex < stackPrefetch.indicesToRequest.length) { - if (lowerIndex >= 0 ) { - nextImageIdIndex = stackPrefetch.indicesToRequest[lowerIndex--]; - imageId = stack.imageIds[nextImageIdIndex]; - requestPoolManager.addRequest(element, imageId, requestType, preventCache, doneCallback, failCallback); - } - - if (higherIndex < stackPrefetch.indicesToRequest.length) { - nextImageIdIndex = stackPrefetch.indicesToRequest[higherIndex++]; - imageId = stack.imageIds[nextImageIdIndex]; - requestPoolManager.addRequest(element, imageId, requestType, preventCache, doneCallback, failCallback); - } - } - - // Try to start the requestPool's grabbing procedure - // in case it isn't already running - requestPoolManager.startGrabbing(); - } - - function promiseRemovedHandler(e, eventData) { - // When an imagePromise has been pushed out of the cache, re-add its index - // it to the indicesToRequest list so that it will be retrieved later if the - // currentImageIdIndex is changed to an image nearby - var element = e.data.element; - var stackData; - - try { - // It will throw an exception in some cases (eg: thumbnails) - stackData = cornerstoneTools.getToolState(element, 'stack'); - } catch(error) { - return; - } - - if (!stackData || !stackData.data || !stackData.data.length) { - return; - } - - var stack = stackData.data[0]; - var imageIdIndex = stack.imageIds.indexOf(eventData.imageId); - - // Make sure the image that was removed is actually in this stack - // before adding it to the indicesToRequest array - if (imageIdIndex < 0) { - return; - } - - var stackPrefetchData = cornerstoneTools.getToolState(element, toolType); - if (!stackPrefetchData || !stackPrefetchData.data || !stackPrefetchData.data.length) { - return; - } - - stackPrefetchData.data[0].indicesToRequest.push(imageIdIndex); - } - - function onImageUpdated(e) { - // Start prefetching again (after a delay) - // When the user has scrolled to a new image - clearTimeout(resetPrefetchTimeout); - resetPrefetchTimeout = setTimeout(function() { - var element = e.target; - - // If playClip is enabled and the user loads a different series in the viewport - // an exception will be thrown because the element will not be enabled anymore - try { - prefetch(element); - } catch(error) { - return; - } - - }, resetPrefetchDelay); - } - - function enable(element) { - // Clear old prefetch data. Skipping this can cause problems when changing the series inside an element - var stackPrefetchDataArray = cornerstoneTools.getToolState(element, toolType); - stackPrefetchDataArray.data = []; - - // First check that there is stack data available - var stackData = cornerstoneTools.getToolState(element, 'stack'); - if (!stackData || !stackData.data || !stackData.data.length) { - return; - } - - var stack = stackData.data[0]; - - // Check if we are allowed to cache images in this stack - if (stack.preventCache === true) { - console.warn('A stack that should not be cached was given the stackPrefetch'); - return; - } - - // Use the currentImageIdIndex from the stack as the initalImageIdIndex - var stackPrefetchData = { - indicesToRequest: range(0, stack.imageIds.length - 1), - enabled: true, - direction: 1 - }; - - // Remove the currentImageIdIndex from the list to request - var indexOfCurrentImage = stackPrefetchData.indicesToRequest.indexOf(stack.currentImageIdIndex); - stackPrefetchData.indicesToRequest.splice(indexOfCurrentImage, 1); - - cornerstoneTools.addToolState(element, toolType, stackPrefetchData); - - prefetch(element); - - $(element).off('CornerstoneNewImage', onImageUpdated); - $(element).on('CornerstoneNewImage', onImageUpdated); - - $(cornerstone).off('CornerstoneImageCachePromiseRemoved', promiseRemovedHandler); - $(cornerstone).on('CornerstoneImageCachePromiseRemoved', { - element: element - }, promiseRemovedHandler); - } - - function disable(element) { - clearTimeout(resetPrefetchTimeout); - $(element).off('CornerstoneNewImage', onImageUpdated); - - $(cornerstone).off('CornerstoneImageCachePromiseRemoved', promiseRemovedHandler); - - var stackPrefetchData = cornerstoneTools.getToolState(element, toolType); - // If there is actually something to disable, disable it - if (stackPrefetchData && stackPrefetchData.data.length) { - stackPrefetchData.data[0].enabled = false; - - // Clear current prefetch requests from the requestPool - cornerstoneTools.requestPoolManager.clearRequestStack(requestType); - } - } - - function getConfiguration() { - return configuration; - } - - function setConfiguration(config) { - configuration = config; - } - - // module/private exports - cornerstoneTools.stackPrefetch = { - enable: enable, - disable: disable, - getConfiguration: getConfiguration, - setConfiguration: setConfiguration - }; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/stackTools/stackPrefetch.js - -// Begin Source: src/stackTools/stackScroll.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - var mouseDragEventData = { - deltaY: 0 - }; - $(eventData.element).on('CornerstoneToolsMouseDrag', mouseDragEventData, dragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).on('CornerstoneToolsMouseClick', mouseUpCallback); - e.stopImmediatePropagation(); - return false; - } - } - - function mouseWheelCallback(e, eventData) { - var images = -eventData.direction; - cornerstoneTools.scroll(eventData.element, images); - } - - function dragCallback(e, eventData) { - var element = eventData.element; - - var toolData = cornerstoneTools.getToolState(element, 'stack'); - if (!toolData || !toolData.data || !toolData.data.length) { - return; - } - - var stackData = toolData.data[0]; - - var config = cornerstoneTools.stackScroll.getConfiguration(); - - // The Math.max here makes it easier to mouseDrag-scroll small image stacks - var pixelsPerImage = $(element).height() / Math.max(stackData.imageIds.length, 8); - if (config && config.stackScrollSpeed) { - pixelsPerImage = config.stackScrollSpeed; - } - - e.data.deltaY = e.data.deltaY || 0; - e.data.deltaY += eventData.deltaPoints.page.y; - if (Math.abs(e.data.deltaY) >= pixelsPerImage) { - var imageDelta = e.data.deltaY / pixelsPerImage; - var imageIdIndexOffset = Math.round(imageDelta); - var imageDeltaMod = e.data.deltaY % pixelsPerImage; - e.data.deltaY = imageDeltaMod; - cornerstoneTools.scroll(element, imageIdIndexOffset); - } - - return false; // false = causes jquery to preventDefault() and stopPropagation() this event - } - - // module/private exports - cornerstoneTools.stackScroll = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); - cornerstoneTools.stackScrollWheel = cornerstoneTools.mouseWheelTool(mouseWheelCallback); - - var options = { - eventData: { - deltaY: 0 - } - }; - cornerstoneTools.stackScrollTouchDrag = cornerstoneTools.touchDragTool(dragCallback, options); - - function multiTouchDragCallback(e, eventData) { - var config = cornerstoneTools.stackScrollMultiTouch.getConfiguration(); - if (config && config.testPointers(eventData)) { - dragCallback(e, eventData); - } - } - - var configuration = { - testPointers: function(eventData) { - return (eventData.numPointers >= 3); - } - }; - - cornerstoneTools.stackScrollMultiTouch = cornerstoneTools.multiTouchDragTool(multiTouchDragCallback, options); - cornerstoneTools.stackScrollMultiTouch.setConfiguration(configuration); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/stackTools/stackScroll.js - -// Begin Source: src/stackTools/stackScrollKeyboard.js -(function(cornerstoneTools) { - - 'use strict'; - - var keys = { - UP: 38, - DOWN: 40 - }; - - function keyDownCallback(e, eventData) { - var keyCode = eventData.keyCode; - if (keyCode !== keys.UP && keyCode !== keys.DOWN) { - return; - } - - var images = 1; - if (keyCode === keys.DOWN) { - images = -1; - } - - cornerstoneTools.scroll(eventData.element, images); - } - - // module/private exports - cornerstoneTools.stackScrollKeyboard = cornerstoneTools.keyboardTool(keyDownCallback); - -})(cornerstoneTools); - -// End Source; src/stackTools/stackScrollKeyboard.js - -// Begin Source: src/stateManagement/applicationState.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function saveApplicationState(elements) { - // Save imageId-specific tool state data - var appState = { - imageIdToolState: cornerstoneTools.globalImageIdSpecificToolStateManager.saveToolState(), - elementToolState: {}, - elementViewport: {} - }; - - // For each of the given elements, save the viewport and any stack-specific tool data - elements.forEach(function(element) { - var toolStateManager = cornerstoneTools.getElementToolStateManager(element); - if (toolStateManager === cornerstoneTools.globalImageIdSpecificToolStateManager) { - return; - } - - appState.elementToolState[element.id] = toolStateManager.saveToolState(); - - appState.elementViewport[element.id] = cornerstone.getViewport(element); - }); - return appState; - } - - function restoreApplicationState(appState) { - // Make sure t - if (!appState.hasOwnProperty('imageIdToolState') || - !appState.hasOwnProperty('elementToolState') || - !appState.hasOwnProperty('elementViewport')) { - return; - } - - // Restore all the imageId specific tool data - cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState(appState.imageIdToolState); - - Object.keys(appState.elementViewport).forEach(function(elementId) { - // Restore any stack specific tool data - var element = document.getElementById(elementId); - if (!element) { - return; - } - - if (!appState.elementToolState.hasOwnProperty(elementId)) { - return; - } - - var toolStateManager = cornerstoneTools.getElementToolStateManager(element); - if (toolStateManager === cornerstoneTools.globalImageIdSpecificToolStateManager) { - return; - } - - toolStateManager.restoreToolState(appState.elementToolState[elementId]); - - // Restore the saved viewport information - var savedViewport = appState.elementViewport[elementId]; - cornerstone.setViewport(element, savedViewport); - - // Update the element to apply the viewport and tool changes - cornerstone.updateImage(element); - }); - return appState; - } - - cornerstoneTools.appState = { - save: saveApplicationState, - restore: restoreApplicationState - }; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/stateManagement/applicationState.js - -// Begin Source: src/stateManagement/frameOfReferenceStateManager.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This implements a frame-of-reference specific tool state management strategy. This means that - // measurement data are tied to a specific frame of reference UID and only visible to objects using - // that frame-of-reference UID - - function newFrameOfReferenceSpecificToolStateManager() { - var toolState = {}; - - // here we add tool state, this is done by tools as well - // as modules that restore saved state - function addFrameOfReferenceSpecificToolState(frameOfReference, toolType, data) { - // if we don't have any tool state for this frameOfReference, add an empty object - if (toolState.hasOwnProperty(frameOfReference) === false) { - toolState[frameOfReference] = {}; - } - - var frameOfReferenceToolState = toolState[frameOfReference]; - - // if we don't have tool state for this type of tool, add an empty object - if (frameOfReferenceToolState.hasOwnProperty(toolType) === false) { - frameOfReferenceToolState[toolType] = { - data: [] - }; - } - - var toolData = frameOfReferenceToolState[toolType]; - - // finally, add this new tool to the state - toolData.data.push(data); - } - - // here you can get state - used by tools as well as modules - // that save state persistently - function getFrameOfReferenceSpecificToolState(frameOfReference, toolType) { - // if we don't have any tool state for this frame of reference, return undefined - if (toolState.hasOwnProperty(frameOfReference) === false) { - return; - } - - var frameOfReferenceToolState = toolState[frameOfReference]; - - // if we don't have tool state for this type of tool, return undefined - if (frameOfReferenceToolState.hasOwnProperty(toolType) === false) { - return; - } - - var toolData = frameOfReferenceToolState[toolType]; - return toolData; - } - - function removeFrameOfReferenceSpecificToolState(frameOfReference, toolType, data) { - // if we don't have any tool state for this frame of reference, return undefined - if (toolState.hasOwnProperty(frameOfReference) === false) { - return; - } - - var frameOfReferenceToolState = toolState[frameOfReference]; - - // if we don't have tool state for this type of tool, return undefined - if (frameOfReferenceToolState.hasOwnProperty(toolType) === false) { - return; - } - - var toolData = frameOfReferenceToolState[toolType]; - // find this tool data - var indexOfData = -1; - for (var i = 0; i < toolData.data.length; i++) { - if (toolData.data[i] === data) { - indexOfData = i; - } - } - - if (indexOfData !== -1) { - toolData.data.splice(indexOfData, 1); - } - } - - var frameOfReferenceToolStateManager = { - get: getFrameOfReferenceSpecificToolState, - add: addFrameOfReferenceSpecificToolState, - remove: removeFrameOfReferenceSpecificToolState - }; - return frameOfReferenceToolStateManager; - } - - // a global frameOfReferenceSpecificToolStateManager - the most common case is to share 3d information - // between stacks of images - var globalFrameOfReferenceSpecificToolStateManager = newFrameOfReferenceSpecificToolStateManager(); - - // module/private exports - cornerstoneTools.newFrameOfReferenceSpecificToolStateManager = newFrameOfReferenceSpecificToolStateManager; - cornerstoneTools.globalFrameOfReferenceSpecificToolStateManager = globalFrameOfReferenceSpecificToolStateManager; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/stateManagement/frameOfReferenceStateManager.js - -// Begin Source: src/stateManagement/imageIdSpecificStateManager.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - // This implements an imageId specific tool state management strategy. This means that - // measurements data is tied to a specific imageId and only visible for enabled elements - // that are displaying that imageId. - - function newImageIdSpecificToolStateManager() { - var toolState = {}; - - // here we add tool state, this is done by tools as well - // as modules that restore saved state - - function saveImageIdToolState(imageId) { - return toolState[imageId]; - } - - function restoreImageIdToolState(imageId, imageIdToolState) { - toolState[imageId] = imageIdToolState; - } - - function saveToolState() { - return toolState; - } - - function restoreToolState(savedToolState) { - toolState = savedToolState; - } - - // here we add tool state, this is done by tools as well - // as modules that restore saved state - function addImageIdSpecificToolState(element, toolType, data) { - var enabledImage = cornerstone.getEnabledElement(element); - // if we don't have any tool state for this imageId, add an empty object - if (!enabledImage.image || toolState.hasOwnProperty(enabledImage.image.imageId) === false) { - toolState[enabledImage.image.imageId] = {}; - } - - var imageIdToolState = toolState[enabledImage.image.imageId]; - - // if we don't have tool state for this type of tool, add an empty object - if (imageIdToolState.hasOwnProperty(toolType) === false) { - imageIdToolState[toolType] = { - data: [] - }; - } - - var toolData = imageIdToolState[toolType]; - - // finally, add this new tool to the state - toolData.data.push(data); - } - - // here you can get state - used by tools as well as modules - // that save state persistently - function getImageIdSpecificToolState(element, toolType) { - var enabledImage = cornerstone.getEnabledElement(element); - // if we don't have any tool state for this imageId, return undefined - if (!enabledImage.image || toolState.hasOwnProperty(enabledImage.image.imageId) === false) { - return; - } - - var imageIdToolState = toolState[enabledImage.image.imageId]; - - // if we don't have tool state for this type of tool, return undefined - if (imageIdToolState.hasOwnProperty(toolType) === false) { - return; - } - - var toolData = imageIdToolState[toolType]; - return toolData; - } - - // Clears all tool data from this toolStateManager. - function clearImageIdSpecificToolStateManager(element) { - var enabledImage = cornerstone.getEnabledElement(element); - if (!enabledImage.image || toolState.hasOwnProperty(enabledImage.image.imageId) === false) { - return; - } - - delete toolState[enabledImage.image.imageId]; - } - - var imageIdToolStateManager = { - get: getImageIdSpecificToolState, - add: addImageIdSpecificToolState, - clear: clearImageIdSpecificToolStateManager, - saveImageIdToolState: saveImageIdToolState, - restoreImageIdToolState: restoreImageIdToolState, - saveToolState: saveToolState, - restoreToolState: restoreToolState, - toolState: toolState - }; - return imageIdToolStateManager; - } - - // a global imageIdSpecificToolStateManager - the most common case is to share state between all - // visible enabled images - var globalImageIdSpecificToolStateManager = newImageIdSpecificToolStateManager(); - - // module/private exports - cornerstoneTools.newImageIdSpecificToolStateManager = newImageIdSpecificToolStateManager; - cornerstoneTools.globalImageIdSpecificToolStateManager = globalImageIdSpecificToolStateManager; - -})(cornerstone, cornerstoneTools); - -// End Source; src/stateManagement/imageIdSpecificStateManager.js - -// Begin Source: src/stateManagement/loadHandlerManager.js -(function(cornerstoneTools) { - - 'use strict'; - - function loadHandlerManager() { - var defaultStartLoadHandler, - defaultEndLoadHandler, - defaultErrorLoadingHandler; - - function setStartLoadHandler(handler) { - defaultStartLoadHandler = handler; - } - - function getStartLoadHandler() { - return defaultStartLoadHandler; - } - - function setEndLoadHandler(handler) { - defaultEndLoadHandler = handler; - } - - function getEndLoadHandler(){ - return defaultEndLoadHandler; - } - - function setErrorLoadingHandler(handler) { - defaultErrorLoadingHandler = handler; - } - - function getErrorLoadingHandler() { - return defaultErrorLoadingHandler; - } - - var loadHandlers = { - setStartLoadHandler: setStartLoadHandler, - getStartLoadHandler: getStartLoadHandler, - setEndLoadHandler: setEndLoadHandler, - getEndLoadHandler: getEndLoadHandler, - setErrorLoadingHandler: setErrorLoadingHandler, - getErrorLoadingHandler: getErrorLoadingHandler - }; - - return loadHandlers; - } - - // module/private exports - cornerstoneTools.loadHandlerManager = loadHandlerManager(); - -})(cornerstoneTools); - -// End Source; src/stateManagement/loadHandlerManager.js - -// Begin Source: src/stateManagement/stackSpecificStateManager.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - // This implements an Stack specific tool state management strategy. This means - // that tool data is shared between all imageIds in a given stack - function newStackSpecificToolStateManager(toolTypes, oldStateManager) { - var toolState = {}; - - function saveToolState() { - return toolState; - } - - function restoreToolState(stackToolState) { - toolState = stackToolState; - } - - // here we add tool state, this is done by tools as well - // as modules that restore saved state - function addStackSpecificToolState(element, toolType, data) { - // if this is a tool type to apply to the stack, do so - if (toolTypes.indexOf(toolType) >= 0) { - - // if we don't have tool state for this type of tool, add an empty object - if (toolState.hasOwnProperty(toolType) === false) { - toolState[toolType] = { - data: [] - }; - } - - var toolData = toolState[toolType]; - - // finally, add this new tool to the state - toolData.data.push(data); - } else { - // call the imageId specific tool state manager - return oldStateManager.add(element, toolType, data); - } - } - - // here you can get state - used by tools as well as modules - // that save state persistently - function getStackSpecificToolState(element, toolType) { - // if this is a tool type to apply to the stack, do so - if (toolTypes.indexOf(toolType) >= 0) { - // if we don't have tool state for this type of tool, add an empty object - if (toolState.hasOwnProperty(toolType) === false) { - toolState[toolType] = { - data: [] - }; - } - - var toolData = toolState[toolType]; - return toolData; - } else { - // call the imageId specific tool state manager - return oldStateManager.get(element, toolType); - } - } - - var stackSpecificToolStateManager = { - get: getStackSpecificToolState, - add: addStackSpecificToolState, - saveToolState: saveToolState, - restoreToolState: restoreToolState, - toolState: toolState, - }; - return stackSpecificToolStateManager; - } - - var stackStateManagers = []; - - function addStackStateManager(element, otherTools) { - var oldStateManager = cornerstoneTools.getElementToolStateManager(element); - if (!oldStateManager) { - oldStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager; - } - - var stackTools = [ 'stack', 'stackPrefetch', 'playClip', 'volume', 'slab', 'referenceLines', 'crosshairs' ]; - if (otherTools) { - stackTools = stackTools.concat(otherTools); - } - - var stackSpecificStateManager = cornerstoneTools.newStackSpecificToolStateManager(stackTools, oldStateManager); - stackStateManagers.push(stackSpecificStateManager); - cornerstoneTools.setElementToolStateManager(element, stackSpecificStateManager); - } - - // module/private exports - cornerstoneTools.newStackSpecificToolStateManager = newStackSpecificToolStateManager; - cornerstoneTools.addStackStateManager = addStackStateManager; - -})(cornerstone, cornerstoneTools); - -// End Source; src/stateManagement/stackSpecificStateManager.js - -// Begin Source: src/stateManagement/textStyleManager.js -(function(cornerstoneTools) { - - 'use strict'; - - function textStyleManager() { - var defaultFontSize = 15, - defaultFont = defaultFontSize + 'px Arial', - defaultBackgroundColor = 'transparent'; - - function setFont(font) { - defaultFont = font; - } - - function getFont() { - return defaultFont; - } - - function setFontSize(fontSize) { - defaultFontSize = fontSize; - } - - function getFontSize() { - return defaultFontSize; - } - - function setBackgroundColor(backgroundColor) { - defaultBackgroundColor = backgroundColor; - } - - function getBackgroundColor() { - return defaultBackgroundColor; - } - - var textStyle = { - setFont: setFont, - getFont: getFont, - setFontSize: setFontSize, - getFontSize: getFontSize, - setBackgroundColor: setBackgroundColor, - getBackgroundColor: getBackgroundColor - }; - - return textStyle; - } - - // module/private exports - cornerstoneTools.textStyle = textStyleManager(); - -})(cornerstoneTools); - -// End Source; src/stateManagement/textStyleManager.js - -// Begin Source: src/stateManagement/timeSeriesSpecificStateManager.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - // This implements an Stack specific tool state management strategy. This means - // that tool data is shared between all imageIds in a given stack - function newTimeSeriesSpecificToolStateManager(toolTypes, oldStateManager) { - var toolState = {}; - - // here we add tool state, this is done by tools as well - // as modules that restore saved state - function addStackSpecificToolState(element, toolType, data) { - // if this is a tool type to apply to the stack, do so - if (toolTypes.indexOf(toolType) >= 0) { - - // if we don't have tool state for this type of tool, add an empty object - if (toolState.hasOwnProperty(toolType) === false) { - toolState[toolType] = { - data: [] - }; - } - - var toolData = toolState[toolType]; - - // finally, add this new tool to the state - toolData.data.push(data); - } else { - // call the imageId specific tool state manager - return oldStateManager.add(element, toolType, data); - } - } - - // here you can get state - used by tools as well as modules - // that save state persistently - function getStackSpecificToolState(element, toolType) { - // if this is a tool type to apply to the stack, do so - if (toolTypes.indexOf(toolType) >= 0) { - // if we don't have tool state for this type of tool, add an empty object - if (toolState.hasOwnProperty(toolType) === false) { - toolState[toolType] = { - data: [] - }; - } - - var toolData = toolState[toolType]; - return toolData; - } else { - // call the imageId specific tool state manager - return oldStateManager.get(element, toolType); - } - } - - var imageIdToolStateManager = { - get: getStackSpecificToolState, - add: addStackSpecificToolState - }; - return imageIdToolStateManager; - } - - var timeSeriesStateManagers = []; - - function addTimeSeriesStateManager(element, tools) { - tools = tools || [ 'timeSeries' ]; - var oldStateManager = cornerstoneTools.getElementToolStateManager(element); - if (oldStateManager === undefined) { - oldStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager; - } - - var timeSeriesSpecificStateManager = cornerstoneTools.newTimeSeriesSpecificToolStateManager(tools, oldStateManager); - timeSeriesStateManagers.push(timeSeriesSpecificStateManager); - cornerstoneTools.setElementToolStateManager(element, timeSeriesSpecificStateManager); - } - - // module/private exports - cornerstoneTools.newTimeSeriesSpecificToolStateManager = newTimeSeriesSpecificToolStateManager; - cornerstoneTools.addTimeSeriesStateManager = addTimeSeriesStateManager; - -})(cornerstone, cornerstoneTools); - -// End Source; src/stateManagement/timeSeriesSpecificStateManager.js - -// Begin Source: src/stateManagement/toolColorManager.js -(function(cornerstoneTools) { - - 'use strict'; - - function toolColorManager() { - var defaultColor = 'white', - activeColor = 'greenyellow', - fillColor = 'transparent'; - - function setFillColor(color) { - fillColor = color; - } - - function getFillColor() { - return fillColor; - } - - function setToolColor(color) { - defaultColor = color; - } - - function getToolColor() { - return defaultColor; - } - - function setActiveToolColor(color) { - activeColor = color; - } - - function getActiveToolColor() { - return activeColor; - } - - function getColorIfActive(active) { - return active ? activeColor : defaultColor; - } - - var toolColors = { - setFillColor: setFillColor, - getFillColor: getFillColor, - setToolColor: setToolColor, - getToolColor: getToolColor, - setActiveColor: setActiveToolColor, - getActiveColor: getActiveToolColor, - getColorIfActive: getColorIfActive - }; - - return toolColors; - } - - // module/private exports - cornerstoneTools.toolColors = toolColorManager(); - -})(cornerstoneTools); - -// End Source; src/stateManagement/toolColorManager.js - -// Begin Source: src/stateManagement/toolCoordinateManager.js -(function(cornerstoneTools) { - - 'use strict'; - - function toolCoordinateManager(){ - var cooordsData = ''; - - function setActiveToolCoords(eventData){ - cooordsData = eventData.currentPoints.canvas; - } - - function getActiveToolCoords(){ - return cooordsData; - } - - var toolCoords = { - setCoords: setActiveToolCoords, - getCoords: getActiveToolCoords - }; - - return toolCoords; - } - - // module/private exports - cornerstoneTools.toolCoordinates = toolCoordinateManager(); - -})(cornerstoneTools); - -// End Source; src/stateManagement/toolCoordinateManager.js - -// Begin Source: src/stateManagement/toolStateManager.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - function getElementToolStateManager(element) { - var enabledImage = cornerstone.getEnabledElement(element); - // if the enabledImage has no toolStateManager, create a default one for it - // NOTE: This makes state management element specific - if (enabledImage.toolStateManager === undefined) { - enabledImage.toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager; - } - - return enabledImage.toolStateManager; - } - - // here we add tool state, this is done by tools as well - // as modules that restore saved state - function addToolState(element, toolType, data) { - var toolStateManager = getElementToolStateManager(element); - toolStateManager.add(element, toolType, data); - - var eventType = 'CornerstoneToolsMeasurementAdded'; - var eventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, eventData); - // TODO: figure out how to broadcast this change to all enabled elements so they can update the image - // if this change effects them - } - - // here you can get state - used by tools as well as modules - // that save state persistently - function getToolState(element, toolType) { - var toolStateManager = getElementToolStateManager(element); - return toolStateManager.get(element, toolType); - } - - function removeToolState(element, toolType, data) { - var toolStateManager = getElementToolStateManager(element); - var toolData = toolStateManager.get(element, toolType); - // find this tool data - var indexOfData = -1; - for (var i = 0; i < toolData.data.length; i++) { - if (toolData.data[i] === data) { - indexOfData = i; - } - } - - if (indexOfData !== -1) { - toolData.data.splice(indexOfData, 1); - - var eventType = 'CornerstoneToolsMeasurementRemoved'; - var eventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, eventData); - } - } - - function clearToolState(element, toolType) { - var toolStateManager = getElementToolStateManager(element); - var toolData = toolStateManager.get(element, toolType); - - // If any toolData actually exists, clear it - if (toolData !== undefined) { - toolData.data = []; - } - } - - // sets the tool state manager for an element - function setElementToolStateManager(element, toolStateManager) { - var enabledImage = cornerstone.getEnabledElement(element); - enabledImage.toolStateManager = toolStateManager; - } - - // module/private exports - cornerstoneTools.addToolState = addToolState; - cornerstoneTools.getToolState = getToolState; - cornerstoneTools.removeToolState = removeToolState; - cornerstoneTools.clearToolState = clearToolState; - cornerstoneTools.setElementToolStateManager = setElementToolStateManager; - cornerstoneTools.getElementToolStateManager = getElementToolStateManager; - -})(cornerstone, cornerstoneTools); - -// End Source; src/stateManagement/toolStateManager.js - -// Begin Source: src/stateManagement/toolStyleManager.js -(function(cornerstoneTools) { - - 'use strict'; - - function toolStyleManager() { - var defaultWidth = 1, - activeWidth = 2; - - function setToolWidth(width){ - defaultWidth = width; - } - - function getToolWidth(){ - return defaultWidth; - } - - function setActiveToolWidth(width){ - activeWidth = width; - } - - function getActiveToolWidth(){ - return activeWidth; - } - - var toolStyle = { - setToolWidth: setToolWidth, - getToolWidth: getToolWidth, - setActiveWidth: setActiveToolWidth, - getActiveWidth: getActiveToolWidth - }; - - return toolStyle; - } - - // module/private exports - cornerstoneTools.toolStyle = toolStyleManager(); - -})(cornerstoneTools); - -// End Source; src/stateManagement/toolStyleManager.js - -// Begin Source: src/synchronization/panZoomSynchronizer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This function synchronizes the target zoom and pan to match the source - function panZoomSynchronizer(synchronizer, sourceElement, targetElement) { - - // ignore the case where the source and target are the same enabled element - if (targetElement === sourceElement) { - return; - } - // get the source and target viewports - var sourceViewport = cornerstone.getViewport(sourceElement); - var targetViewport = cornerstone.getViewport(targetElement); - - // do nothing if the scale and translation are the same - if (targetViewport.scale === sourceViewport.scale && targetViewport.translation.x === sourceViewport.translation.x && targetViewport.translation.y === sourceViewport.translation.y) { - return; - } - - // scale and/or translation are different, sync them - targetViewport.scale = sourceViewport.scale; - targetViewport.translation.x = sourceViewport.translation.x; - targetViewport.translation.y = sourceViewport.translation.y; - synchronizer.setViewport(targetElement, targetViewport); - } - - // module/private exports - cornerstoneTools.panZoomSynchronizer = panZoomSynchronizer; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/synchronization/panZoomSynchronizer.js - -// Begin Source: src/synchronization/stackImageIndexSynchronizer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This function causes the image in the target stack to be set to the one closest - // to the image in the source stack by image position - function stackImageIndexSynchronizer(synchronizer, sourceElement, targetElement) { - - // ignore the case where the source and target are the same enabled element - if (targetElement === sourceElement) { - return; - } - - var sourceStackToolDataSource = cornerstoneTools.getToolState(sourceElement, 'stack'); - var sourceStackData = sourceStackToolDataSource.data[0]; - var targetStackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack'); - var targetStackData = targetStackToolDataSource.data[0]; - - var newImageIdIndex = sourceStackData.currentImageIdIndex; - - // clamp the index - newImageIdIndex = Math.min(Math.max(newImageIdIndex, 0), targetStackData.imageIds.length - 1); - - // Do nothing if the index has not changed - if (newImageIdIndex === targetStackData.currentImageIdIndex) { - return; - } - - var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); - var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler(); - var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - - if (startLoadingHandler) { - startLoadingHandler(targetElement); - } - - var loader; - if (targetStackData.preventCache === true) { - loader = cornerstone.loadImage(targetStackData.imageIds[newImageIdIndex]); - } else { - loader = cornerstone.loadAndCacheImage(targetStackData.imageIds[newImageIdIndex]); - } - - loader.then(function(image) { - var viewport = cornerstone.getViewport(targetElement); - targetStackData.currentImageIdIndex = newImageIdIndex; - synchronizer.displayImage(targetElement, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(targetElement, image); - } - }, function(error) { - var imageId = targetStackData.imageIds[newImageIdIndex]; - if (errorLoadingHandler) { - errorLoadingHandler(targetElement, imageId, error); - } - }); - } - - // module/private exports - cornerstoneTools.stackImageIndexSynchronizer = stackImageIndexSynchronizer; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/synchronization/stackImageIndexSynchronizer.js - -// Begin Source: src/synchronization/stackImagePositionOffsetSynchronizer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This function causes the image in the target stack to be set to the one closest - // to the image in the source stack by image position - - // In the future we will want to have a way to manually register links sets of the same orientation (e.g. an axial link set from a prior with an axial link set of a current). The user could do this by scrolling the two stacks to a similar location and then doing a user action (e.g. right click link) at which point the system will capture the delta between the image position (patient) of both stacks and use that to sync them. This offset will need to be adjustable. - - function stackImagePositionOffsetSynchronizer(synchronizer, sourceElement, targetElement, eventData, positionDifference) { - - // ignore the case where the source and target are the same enabled element - if (targetElement === sourceElement) { - return; - } - - var sourceEnabledElement = cornerstone.getEnabledElement(sourceElement); - var sourceImagePlane = cornerstoneTools.metaData.get('imagePlane', sourceEnabledElement.image.imageId); - var sourceImagePosition = sourceImagePlane.imagePositionPatient; - - var stackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack'); - var stackData = stackToolDataSource.data[0]; - - var minDistance = Number.MAX_VALUE; - var newImageIdIndex = -1; - - if (!positionDifference) { - return; - } - - var finalPosition = sourceImagePosition.clone().add(positionDifference); - - stackData.imageIds.forEach(function(imageId, index) { - var imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId); - var imagePosition = imagePlane.imagePositionPatient; - var distance = finalPosition.distanceToSquared(imagePosition); - - if (distance < minDistance) { - minDistance = distance; - newImageIdIndex = index; - } - }); - - if (newImageIdIndex === stackData.currentImageIdIndex || newImageIdIndex === -1) { - return; - } - - var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); - var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler(); - var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - - if (startLoadingHandler) { - startLoadingHandler(targetElement); - } - - var loader; - if (stackData.preventCache === true) { - loader = cornerstone.loadImage(stackData.imageIds[newImageIdIndex]); - } else { - loader = cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]); - } - - loader.then(function(image) { - var viewport = cornerstone.getViewport(targetElement); - stackData.currentImageIdIndex = newImageIdIndex; - synchronizer.displayImage(targetElement, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(targetElement, image); - } - }, function(error) { - var imageId = stackData.imageIds[newImageIdIndex]; - if (errorLoadingHandler) { - errorLoadingHandler(targetElement, imageId, error); - } - }); - } - - // module/private exports - cornerstoneTools.stackImagePositionOffsetSynchronizer = stackImagePositionOffsetSynchronizer; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/synchronization/stackImagePositionOffsetSynchronizer.js - -// Begin Source: src/synchronization/stackImagePositionSynchronizer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This function causes the image in the target stack to be set to the one closest - // to the image in the source stack by image position - function stackImagePositionSynchronizer(synchronizer, sourceElement, targetElement) { - - // ignore the case where the source and target are the same enabled element - if (targetElement === sourceElement) { - return; - } - - var sourceImage = cornerstone.getEnabledElement(sourceElement).image; - var sourceImagePlane = cornerstoneTools.metaData.get('imagePlane', sourceImage.imageId); - var sourceImagePosition = sourceImagePlane.imagePositionPatient; - - var stackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack'); - var stackData = stackToolDataSource.data[0]; - - var minDistance = Number.MAX_VALUE; - var newImageIdIndex = -1; - - $.each(stackData.imageIds, function(index, imageId) { - var imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId); - var imagePosition = imagePlane.imagePositionPatient; - var distance = imagePosition.distanceToSquared(sourceImagePosition); - //console.log(index + '=' + distance); - if (distance < minDistance) { - minDistance = distance; - newImageIdIndex = index; - } - }); - - if (newImageIdIndex === stackData.currentImageIdIndex) { - return; - } - - var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); - var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler(); - var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - - if (startLoadingHandler) { - startLoadingHandler(targetElement); - } - - if (newImageIdIndex !== -1) { - var loader; - if (stackData.preventCache === true) { - loader = cornerstone.loadImage(stackData.imageIds[newImageIdIndex]); - } else { - loader = cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]); - } - - loader.then(function(image) { - var viewport = cornerstone.getViewport(targetElement); - stackData.currentImageIdIndex = newImageIdIndex; - synchronizer.displayImage(targetElement, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(targetElement, image); - } - }, function(error) { - var imageId = stackData.imageIds[newImageIdIndex]; - if (errorLoadingHandler) { - errorLoadingHandler(targetElement, imageId, error); - } - }); - } - } - - // module/private exports - cornerstoneTools.stackImagePositionSynchronizer = stackImagePositionSynchronizer; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/synchronization/stackImagePositionSynchronizer.js - -// Begin Source: src/synchronization/stackScrollSynchronizer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This function causes any scrolling actions within the stack to propagate to - // all of the other viewports that are synced - function stackScrollSynchronizer(synchronizer, sourceElement, targetElement, eventData) { - // If the target and source are the same, stop - if (sourceElement === targetElement) { - return; - } - - // If there is no event, or direction is 0, stop - if (!eventData || !eventData.direction) { - return; - } - - // Get the stack of the target viewport - var stackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack'); - var stackData = stackToolDataSource.data[0]; - - // Get the new index for the stack - var newImageIdIndex = stackData.currentImageIdIndex + eventData.direction; - - // Ensure the index does not exceed the bounds of the stack - newImageIdIndex = Math.min(Math.max(newImageIdIndex, 0), stackData.imageIds.length - 1); - - // If the index has not changed, stop here - if (stackData.currentImageIdIndex === newImageIdIndex) { - return; - } - - var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); - var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler(); - var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - - if (startLoadingHandler) { - startLoadingHandler(targetElement); - } - - var loader; - if (stackData.preventCache === true) { - loader = cornerstone.loadImage(stackData.imageIds[newImageIdIndex]); - } else { - loader = cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]); - } - - loader.then(function(image) { - var viewport = cornerstone.getViewport(targetElement); - stackData.currentImageIdIndex = newImageIdIndex; - synchronizer.displayImage(targetElement, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(targetElement, image); - } - }, function(error) { - var imageId = stackData.imageIds[newImageIdIndex]; - if (errorLoadingHandler) { - errorLoadingHandler(targetElement, imageId, error); - } - }); - } - - // module/private exports - cornerstoneTools.stackScrollSynchronizer = stackScrollSynchronizer; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/synchronization/stackScrollSynchronizer.js - -// Begin Source: src/synchronization/synchronizer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This object is responsible for synchronizing target elements when an event fires on a source - // element - function Synchronizer(event, handler) { - - var that = this; - var sourceElements = []; // source elements fire the events we want to synchronize to - var targetElements = []; // target elements we want to synchronize to source elements - - var ignoreFiredEvents = false; - var initialData = {}; - var eventHandler = handler; - - this.setHandler = function(handler) { - eventHandler = handler; - }; - - this.getHandler = function() { - return eventHandler; - }; - - this.getDistances = function() { - if (!sourceElements.length || !targetElements.length) { - return; - } - - initialData.distances = {}; - initialData.imageIds = { - sourceElements: [], - targetElements: [] - }; - - sourceElements.forEach(function(sourceElement) { - var sourceEnabledElement = cornerstone.getEnabledElement(sourceElement); - if (!sourceEnabledElement || !sourceEnabledElement.image) { - return; - } - - var sourceImageId = sourceEnabledElement.image.imageId; - var sourceImagePlane = cornerstoneTools.metaData.get('imagePlane', sourceImageId); - if (!sourceImagePlane || !sourceImagePlane.imagePositionPatient) { - return; - } - - var sourceImagePosition = sourceImagePlane.imagePositionPatient; - - if (initialData.hasOwnProperty(sourceEnabledElement)) { - return; - } else { - initialData.distances[sourceImageId] = {}; - } - - initialData.imageIds.sourceElements.push(sourceImageId); - - targetElements.forEach(function(targetElement) { - var targetEnabledElement = cornerstone.getEnabledElement(targetElement); - if (!targetEnabledElement || !targetEnabledElement.image) { - return; - } - - var targetImageId = targetEnabledElement.image.imageId; - - initialData.imageIds.targetElements.push(targetImageId); - - if (sourceElement === targetElement) { - return; - } - - if (sourceImageId === targetImageId) { - return; - } - - if (initialData.distances[sourceImageId].hasOwnProperty(targetImageId)) { - return; - } - - var targetImagePlane = cornerstoneTools.metaData.get('imagePlane', targetImageId); - if (!targetImagePlane || !targetImagePlane.imagePositionPatient) { - return; - } - - var targetImagePosition = targetImagePlane.imagePositionPatient; - - initialData.distances[sourceImageId][targetImageId] = targetImagePosition.clone().sub(sourceImagePosition); - }); - - if (!Object.keys(initialData.distances[sourceImageId]).length) { - delete initialData.distances[sourceImageId]; - } - }); - }; - - function fireEvent(sourceElement, eventData) { - // Broadcast an event that something changed - if (!sourceElements.length || !targetElements.length) { - return; - } - - ignoreFiredEvents = true; - targetElements.forEach(function(targetElement) { - var targetIndex = targetElements.indexOf(targetElement); - if (targetIndex === -1) { - return; - } - - var targetImageId = initialData.imageIds.targetElements[targetIndex]; - var sourceIndex = sourceElements.indexOf(sourceElement); - if (sourceIndex === -1) { - return; - } - - var sourceImageId = initialData.imageIds.sourceElements[sourceIndex]; - - var positionDifference; - if (sourceImageId === targetImageId) { - positionDifference = 0; - } else { - positionDifference = initialData.distances[sourceImageId][targetImageId]; - } - - eventHandler(that, sourceElement, targetElement, eventData, positionDifference); - }); - ignoreFiredEvents = false; - } - - function onEvent(e, eventData) { - if (ignoreFiredEvents === true) { - return; - } - - fireEvent(e.currentTarget, eventData); - } - - // adds an element as a source - this.addSource = function(element) { - // Return if this element was previously added - var index = sourceElements.indexOf(element); - if (index !== -1) { - return; - } - - // Add to our list of enabled elements - sourceElements.push(element); - - // subscribe to the event - $(element).on(event, onEvent); - - // Update the inital distances between elements - that.getDistances(); - - that.updateDisableHandlers(); - }; - - // adds an element as a target - this.addTarget = function(element) { - // Return if this element was previously added - var index = targetElements.indexOf(element); - if (index !== -1) { - return; - } - - // Add to our list of enabled elements - targetElements.push(element); - - // Update the inital distances between elements - that.getDistances(); - - // Invoke the handler for this new target element - eventHandler(that, element, element, 0); - - that.updateDisableHandlers(); - }; - - // adds an element as both a source and a target - this.add = function(element) { - that.addSource(element); - that.addTarget(element); - }; - - // removes an element as a source - this.removeSource = function(element) { - // Find the index of this element - var index = sourceElements.indexOf(element); - if (index === -1) { - return; - } - - // remove this element from the array - sourceElements.splice(index, 1); - - // stop listening for the event - $(element).off(event, onEvent); - - // Update the inital distances between elements - that.getDistances(); - - // Update everyone listening for events - fireEvent(element); - that.updateDisableHandlers(); - }; - - // removes an element as a target - this.removeTarget = function(element) { - // Find the index of this element - var index = targetElements.indexOf(element); - if (index === -1) { - return; - } - - // remove this element from the array - targetElements.splice(index, 1); - - // Update the inital distances between elements - that.getDistances(); - - // Invoke the handler for the removed target - eventHandler(that, element, element, 0); - that.updateDisableHandlers(); - }; - - // removes an element as both a source and target - this.remove = function(element) { - that.removeTarget(element); - that.removeSource(element); - }; - - // returns the source elements - this.getSourceElements = function() { - return sourceElements; - }; - - // returns the target elements - this.getTargetElements = function() { - return targetElements; - }; - - this.displayImage = function(element, image, viewport) { - ignoreFiredEvents = true; - cornerstone.displayImage(element, image, viewport); - ignoreFiredEvents = false; - }; - - this.setViewport = function(element, viewport) { - ignoreFiredEvents = true; - cornerstone.setViewport(element, viewport); - ignoreFiredEvents = false; - }; - - function disableHandler(e, eventData) { - var element = eventData.element; - that.remove(element); - } - - this.updateDisableHandlers = function() { - var elements = $.unique(sourceElements.concat(targetElements)); - elements.forEach(function(element) { - $(element).off('CornerstoneElementDisabled', disableHandler); - $(element).on('CornerstoneElementDisabled', disableHandler); - }); - }; - - this.destroy = function() { - var elements = $.unique(sourceElements.concat(targetElements)); - elements.forEach(function(element) { - that.remove(element); - }); - }; - } - - // module/private exports - cornerstoneTools.Synchronizer = Synchronizer; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/synchronization/synchronizer.js - -// Begin Source: src/synchronization/updateImageSynchronizer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This function causes the target image to be drawn immediately - function updateImageSynchronizer(synchronizer, sourceElement, targetElement) { - - // ignore the case where the source and target are the same enabled element - if (targetElement === sourceElement) { - return; - } - - cornerstone.updateImage(targetElement); - } - - // module/private exports - cornerstoneTools.updateImageSynchronizer = updateImageSynchronizer; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/synchronization/updateImageSynchronizer.js - -// Begin Source: src/synchronization/wwwcSynchronizer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // This function synchronizes the target element ww/wc to match the source element - function wwwcSynchronizer(synchronizer, sourceElement, targetElement) { - - // ignore the case where the source and target are the same enabled element - if (targetElement === sourceElement) { - return; - } - // get the source and target viewports - var sourceViewport = cornerstone.getViewport(sourceElement); - var targetViewport = cornerstone.getViewport(targetElement); - - // do nothing if the ww/wc already match - if (targetViewport.voi.windowWidth === sourceViewport.voi.windowWidth && targetViewport.voi.windowCenter === sourceViewport.voi.windowCenter && targetViewport.invert === sourceViewport.invert) { - return; - } - - // www/wc are different, sync them - targetViewport.voi.windowWidth = sourceViewport.voi.windowWidth; - targetViewport.voi.windowCenter = sourceViewport.voi.windowCenter; - targetViewport.invert = sourceViewport.invert; - synchronizer.setViewport(targetElement, targetViewport); - } - - // module/private exports - cornerstoneTools.wwwcSynchronizer = wwwcSynchronizer; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/synchronization/wwwcSynchronizer.js - -// Begin Source: src/timeSeriesTools/ProbeTool4D.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var toolType = 'probe4D'; - - function updateLineSample(measurementData) { - var samples = []; - - measurementData.timeSeries.stacks.forEach(function(stack) { - var loader; - if (stack.preventCache === true) { - loader = cornerstone.loadImage(stack.imageIds[measurementData.imageIdIndex]); - } else { - loader = cornerstone.loadAndCacheImage(stack.imageIds[measurementData.imageIdIndex]); - } - - loader.then(function(image) { - var offset = Math.round(measurementData.handles.end.x) + Math.round(measurementData.handles.end.y) * image.width; - var sample = image.getPixelData()[offset]; - samples.push(sample); - //console.log(sample); - }); - }); - measurementData.lineSample.set(samples); - } - - ///////// BEGIN ACTIVE TOOL /////// - function createNewMeasurement(mouseEventData) { - var timeSeriestoolData = cornerstoneTools.getToolState(mouseEventData.element, 'timeSeries'); - if (timeSeriestoolData === undefined || timeSeriestoolData.data === undefined || timeSeriestoolData.data.length === 0) { - return; - } - - var timeSeries = timeSeriestoolData.data[0]; - - // create the measurement data for this tool with the end handle activated - var measurementData = { - timeSeries: timeSeries, - lineSample: new cornerstoneTools.LineSampleMeasurement(), - imageIdIndex: timeSeries.stacks[timeSeries.currentStackIndex].currentImageIdIndex, - visible: true, - handles: { - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true - } - } - }; - updateLineSample(measurementData); - cornerstoneTools.MeasurementManager.add(measurementData); - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - ///////// BEGIN IMAGE RENDERING /////// - - function onImageRendered(e, eventData) { - - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (toolData === undefined) { - return; - } - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, context); - var color = 'white'; - - for (var i = 0; i < toolData.data.length; i++) { - context.save(); - var data = toolData.data[i]; - - // draw the handles - context.beginPath(); - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - context.stroke(); - - // Draw text - var fontParameters = cornerstoneTools.setContextToDisplayFontSize(eventData.enabledElement, eventData.canvasContext, 15); - context.font = '' + fontParameters.fontSize + 'px Arial'; - - // translate the x/y away from the cursor - var x = Math.round(data.handles.end.x); - var y = Math.round(data.handles.end.y); - var textX = data.handles.end.x + 3; - var textY = data.handles.end.y - 3; - - context.fillStyle = color; - - context.fillText('' + x + ',' + y, textX, textY); - - context.restore(); - } - } - ///////// END IMAGE RENDERING /////// - - // module exports - cornerstoneTools.probeTool4D = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - toolType: toolType - }); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/timeSeriesTools/ProbeTool4D.js - -// Begin Source: src/timeSeriesTools/timeSeries.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function incrementTimePoint(element, timePoints, wrap) { - var toolData = cornerstoneTools.getToolState(element, 'timeSeries'); - if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) { - return; - } - - var timeSeriesData = toolData.data[0]; - var currentStack = timeSeriesData.stacks[timeSeriesData.currentStackIndex]; - var currentImageIdIndex = currentStack.currentImageIdIndex; - var newStackIndex = timeSeriesData.currentStackIndex + timePoints; - - // loop around if we go outside the stack - if (wrap) { - if (newStackIndex >= timeSeriesData.stacks.length) { - newStackIndex = 0; - } - - if (newStackIndex < 0) { - newStackIndex = timeSeriesData.stacks.length - 1; - } - } else { - newStackIndex = Math.min(timeSeriesData.stacks.length - 1, newStackIndex); - newStackIndex = Math.max(0, newStackIndex); - } - - if (newStackIndex !== timeSeriesData.currentStackIndex) { - var viewport = cornerstone.getViewport(element); - var newStack = timeSeriesData.stacks[newStackIndex]; - - var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); - var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler(); - var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - - if (startLoadingHandler) { - startLoadingHandler(element); - } - - var loader; - if (newStack.preventCache === true) { - loader = cornerstone.loadImage(newStack.imageIds[currentImageIdIndex]); - } else { - loader = cornerstone.loadAndCacheImage(newStack.imageIds[currentImageIdIndex]); - } - - loader.then(function(image) { - if (timeSeriesData.currentImageIdIndex !== currentImageIdIndex) { - newStack.currentImageIdIndex = currentImageIdIndex; - timeSeriesData.currentStackIndex = newStackIndex; - cornerstone.displayImage(element, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(element, image); - } - } - }, function(error) { - var imageId = newStack.imageIds[currentImageIdIndex]; - if (errorLoadingHandler) { - errorLoadingHandler(element, imageId, error); - } - }); - } - } - - // module/private exports - cornerstoneTools.incrementTimePoint = incrementTimePoint; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/timeSeriesTools/timeSeries.js - -// Begin Source: src/timeSeriesTools/timeSeriesPlayer.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - var toolType = 'timeSeriesPlayer'; - - /** - * Starts playing a clip of different time series of the same image or adjusts the frame rate of an - * already playing clip. framesPerSecond is optional and defaults to 30 if not specified. A negative - * framesPerSecond will play the clip in reverse. - * The element must have time series - * @param element - * @param framesPerSecond - */ - function playClip(element, framesPerSecond) { - if (element === undefined) { - throw 'playClip: element must not be undefined'; - } - - if (framesPerSecond === undefined) { - framesPerSecond = 30; - } - - var timeSeriesToolData = cornerstoneTools.getToolState(element, 'timeSeries'); - if (timeSeriesToolData === undefined || timeSeriesToolData.data === undefined || timeSeriesToolData.data.length === 0) { - return; - } - - var playClipToolData = cornerstoneTools.getToolState(element, toolType); - var playClipData; - if (playClipToolData === undefined || playClipToolData.data.length === 0) { - playClipData = { - intervalId: undefined, - framesPerSecond: framesPerSecond, - lastFrameTimeStamp: undefined, - frameRate: 0 - }; - cornerstoneTools.addToolState(element, toolType, playClipData); - } else { - playClipData = playClipToolData.data[0]; - playClipData.framesPerSecond = framesPerSecond; - } - - // if already playing, do not set a new interval - if (playClipData.intervalId !== undefined) { - return; - } - - playClipData.intervalId = setInterval(function() { - if (playClipData.framesPerSecond > 0) { - cornerstoneTools.incrementTimePoint(element, 1, true); - } else { - cornerstoneTools.incrementTimePoint(element, -1, true); - } - }, 1000 / Math.abs(playClipData.framesPerSecond)); - } - - /** - * Stops an already playing clip. - * * @param element - */ - function stopClip(element) { - var playClipToolData = cornerstoneTools.getToolState(element, toolType); - var playClipData; - if (playClipToolData === undefined || playClipToolData.data.length === 0) { - return; - } else { - playClipData = playClipToolData.data[0]; - } - - clearInterval(playClipData.intervalId); - playClipData.intervalId = undefined; - } - - // module/private exports - cornerstoneTools.timeSeriesPlayer = { - start: playClip, - stop: stopClip - }; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/timeSeriesTools/timeSeriesPlayer.js - -// Begin Source: src/timeSeriesTools/timeSeriesScroll.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function mouseUpCallback(e, eventData) { - $(eventData.element).off('CornerstoneToolsMouseDrag', mouseDragCallback); - $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); - } - - function mouseDownCallback(e, eventData) { - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - - var mouseDragEventData = { - deltaY: 0, - options: e.data.options - }; - $(eventData.element).on('CornerstoneToolsMouseDrag', mouseDragEventData, mouseDragCallback); - $(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(eventData.element).on('CornerstoneToolsMouseClick', mouseUpCallback); - e.stopImmediatePropagation(); - return false; - } - } - - function mouseDragCallback(e, eventData) { - e.data.deltaY += eventData.deltaPoints.page.y; - - var toolData = cornerstoneTools.getToolState(eventData.element, 'timeSeries'); - if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) { - return; - } - - var timeSeriesData = toolData.data[0]; - - var pixelsPerTimeSeries = $(eventData.element).height() / timeSeriesData.stacks.length ; - if (e.data.options !== undefined && e.data.options.timeSeriesScrollSpeed !== undefined) { - pixelsPerTimeSeries = e.data.options.timeSeriesScrollSpeed; - } - - if (e.data.deltaY >= pixelsPerTimeSeries || e.data.deltaY <= -pixelsPerTimeSeries) { - var timeSeriesDelta = Math.round(e.data.deltaY / pixelsPerTimeSeries); - var timeSeriesDeltaMod = e.data.deltaY % pixelsPerTimeSeries; - cornerstoneTools.incrementTimePoint(eventData.element, timeSeriesDelta); - e.data.deltaY = timeSeriesDeltaMod; - } - - return false; // false = cases jquery to preventDefault() and stopPropagation() this event - } - - function mouseWheelCallback(e, eventData) { - var images = -eventData.direction; - cornerstoneTools.incrementTimePoint(eventData.element, images); - } - - function onDrag(e) { - var mouseMoveData = e.originalEvent.detail; - var eventData = { - deltaY: 0 - }; - eventData.deltaY += mouseMoveData.deltaPoints.page.y; - - var toolData = cornerstoneTools.getToolState(mouseMoveData.element, 'stack'); - if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) { - return; - } - - if (eventData.deltaY >= 3 || eventData.deltaY <= -3) { - var timeSeriesDelta = eventData.deltaY / 3; - var timeSeriesDeltaMod = eventData.deltaY % 3; - cornerstoneTools.setTimePoint(eventData.element, timeSeriesDelta); - eventData.deltaY = timeSeriesDeltaMod; - } - - return false; // false = cases jquery to preventDefault() and stopPropagation() this event - } - - // module/private exports - cornerstoneTools.timeSeriesScroll = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); - cornerstoneTools.timeSeriesScrollWheel = cornerstoneTools.mouseWheelTool(mouseWheelCallback); - cornerstoneTools.timeSeriesScrollTouchDrag = cornerstoneTools.touchDragTool(onDrag); - -})($, cornerstone, cornerstoneTools); - -// End Source; src/timeSeriesTools/timeSeriesScroll.js - -// Begin Source: src/util/RoundToDecimal.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - function roundToDecimal(value, precision) { - var multiplier = Math.pow(10, precision); - return (Math.round(value * multiplier) / multiplier); - } - - // module exports - cornerstoneTools.roundToDecimal = roundToDecimal; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/util/RoundToDecimal.js - -// Begin Source: src/util/calculateEllipseStatistics.js -(function(cornerstoneTools) { - - 'use strict'; - - function calculateEllipseStatistics(sp, ellipse) { - // TODO: Get a real statistics library here that supports large counts - - var sum = 0; - var sumSquared = 0; - var count = 0; - var index = 0; - - for (var y = ellipse.top; y < ellipse.top + ellipse.height; y++) { - for (var x = ellipse.left; x < ellipse.left + ellipse.width; x++) { - var point = { - x: x, - y: y - }; - - if (cornerstoneTools.pointInEllipse(ellipse, point)) { - sum += sp[index]; - sumSquared += sp[index] * sp[index]; - count++; - } - - index++; - } - } - - if (count === 0) { - return { - count: count, - mean: 0.0, - variance: 0.0, - stdDev: 0.0 - }; - } - - var mean = sum / count; - var variance = sumSquared / count - mean * mean; - - return { - count: count, - mean: mean, - variance: variance, - stdDev: Math.sqrt(variance) - }; - } - - cornerstoneTools.calculateEllipseStatistics = calculateEllipseStatistics; - -})(cornerstoneTools); - -// End Source; src/util/calculateEllipseStatistics.js - -// Begin Source: src/util/calculateSUV.js -(function(cornerstoneTools) { - - 'use strict'; - - // Returns a decimal value given a fractional value - function fracToDec(fractionalValue) { - return parseFloat('.' + fractionalValue); - } - - function calculateSUV(image, storedPixelValue) { - if (!dicomParser) { - return; - } - - var patientStudyModule = cornerstone.metaData.get('patientStudyModule', image.imageId); - var seriesModule = cornerstone.metaData.get('generalSeriesModule', image.imageId); - if (!patientStudyModule || !seriesModule) { - return; - } - - var modality = seriesModule.modality; - - // image must be PET - if (modality !== 'PT') { - return; - } - - var modalityPixelValue = storedPixelValue * image.slope + image.intercept; - - var patientWeight = patientStudyModule.patientWeight; // in kg - if (!patientWeight) { - return; - } - - var petSequenceModule = cornerstone.metaData.get('petIsotopeModule', image.imageId); - if (!petSequenceModule) { - return; - } - - var radiopharmaceuticalInfo = petSequenceModule.radiopharmaceuticalInfo; - var startTime = radiopharmaceuticalInfo.radiopharmaceuticalStartTime; - var totalDose = radiopharmaceuticalInfo.radionuclideTotalDose; - var halfLife = radiopharmaceuticalInfo.radionuclideHalfLife; - var seriesAcquisitionTime = seriesModule.seriesTime; - - if (!startTime || !totalDose || !halfLife || !seriesAcquisitionTime) { - return; - } - - var acquisitionTimeInSeconds = fracToDec(seriesAcquisitionTime.fractionalSeconds || 0) + seriesAcquisitionTime.seconds + seriesAcquisitionTime.minutes * 60 + seriesAcquisitionTime.hours * 60 * 60; - var injectionStartTimeInSeconds = fracToDec(startTime.fractionalSeconds) + startTime.seconds + startTime.minutes * 60 + startTime.hours * 60 * 60; - var durationInSeconds = acquisitionTimeInSeconds - injectionStartTimeInSeconds; - var correctedDose = totalDose * Math.exp(-durationInSeconds * Math.log(2) / halfLife); - var suv = modalityPixelValue * patientWeight / correctedDose * 1000; - - return suv; - } - - // module exports - cornerstoneTools.calculateSUV = calculateSUV; - -})(cornerstoneTools); - -// End Source; src/util/calculateSUV.js - -// Begin Source: src/util/copyPoints.js -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function copyPoints(points) { - var page = cornerstoneMath.point.copy(points.page); - var image = cornerstoneMath.point.copy(points.image); - var client = cornerstoneMath.point.copy(points.client); - var canvas = cornerstoneMath.point.copy(points.canvas); - return { - page: page, - image: image, - client: client, - canvas: canvas - }; - } - - // module exports - cornerstoneTools.copyPoints = copyPoints; - -})($, cornerstone, cornerstoneMath, cornerstoneTools); - -// End Source; src/util/copyPoints.js - -// Begin Source: src/util/drawArrow.js -(function(cornerstoneTools) { - - 'use strict'; - - function drawArrow(context, start, end, color, lineWidth) { - //variables to be used when creating the arrow - var headLength = 10; - - var angle = Math.atan2(end.y - start.y, end.x - start.x); - - //starting path of the arrow from the start square to the end square and drawing the stroke - context.beginPath(); - context.moveTo(start.x, start.y); - context.lineTo(end.x, end.y); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.stroke(); - - //starting a new path from the head of the arrow to one of the sides of the point - context.beginPath(); - context.moveTo(end.x, end.y); - context.lineTo(end.x - headLength * Math.cos(angle - Math.PI / 7), end.y - headLength * Math.sin(angle - Math.PI / 7)); - - //path from the side point of the arrow, to the other side point - context.lineTo(end.x - headLength * Math.cos(angle + Math.PI / 7), end.y - headLength * Math.sin(angle + Math.PI / 7)); - - //path from the side point back to the tip of the arrow, and then again to the opposite side point - context.lineTo(end.x, end.y); - context.lineTo(end.x - headLength * Math.cos(angle - Math.PI / 7), end.y - headLength * Math.sin(angle - Math.PI / 7)); - - //draws the paths created above - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.stroke(); - context.fillStyle = color; - context.fill(); - } - - // Module exports - cornerstoneTools.drawArrow = drawArrow; - -})(cornerstoneTools); - -// End Source; src/util/drawArrow.js - -// Begin Source: src/util/drawCircle.js -(function(cornerstoneTools) { - - 'use strict'; - - function drawCircle(context, start, color, lineWidth) { - var handleRadius = 6; - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.arc(start.x, start.y, handleRadius, 0, 2 * Math.PI); - context.stroke(); - } - - // Module exports - cornerstoneTools.drawCircle = drawCircle; - -})(cornerstoneTools); - -// End Source; src/util/drawCircle.js - -// Begin Source: src/util/drawEllipse.js -(function(cornerstoneTools) { - - 'use strict'; - - // http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - function drawEllipse(context, x, y, w, h) { - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.beginPath(); - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - context.closePath(); - context.stroke(); - } - - // Module exports - cornerstoneTools.drawEllipse = drawEllipse; - -})(cornerstoneTools); - -// End Source; src/util/drawEllipse.js - -// Begin Source: src/util/drawTextBox.js -(function(cornerstoneTools) { - - 'use strict'; - - function drawTextBox(context, textLines, x, y, color, options) { - if (Object.prototype.toString.call(textLines) !== '[object Array]') { - textLines = [ textLines ]; - } - - var padding = 5, - font = cornerstoneTools.textStyle.getFont(), - fontSize = cornerstoneTools.textStyle.getFontSize(), - backgroundColor = cornerstoneTools.textStyle.getBackgroundColor(); - - context.save(); - context.font = font; - context.textBaseline = 'top'; - context.strokeStyle = color; - - // Find the longest text width in the array of text data - var maxWidth = 0; - textLines.forEach(function(text) { - // Get the text width in the current font - var width = context.measureText(text).width; - - // Find the maximum with for all the text rows; - maxWidth = Math.max(maxWidth, width); - }); - - // Draw the background box with padding - context.fillStyle = backgroundColor; - - // Calculate the bounding box for this text box - var boundingBox = { - width: maxWidth + (padding * 2), - height: padding + textLines.length * (fontSize + padding) - }; - - if (options && options.centering && options.centering.x === true) { - x -= boundingBox.width / 2; - } - - if (options && options.centering && options.centering.y === true) { - y -= boundingBox.height / 2; - } - - boundingBox.left = x; - boundingBox.top = y; - - if (options && options.debug === true) { - context.fillStyle = '#FF0000'; - } - - context.fillRect(boundingBox.left, boundingBox.top, boundingBox.width, boundingBox.height); - - // Draw each of the text lines on top of the background box - textLines.forEach(function(text, index) { - context.fillStyle = color; - - var ypos; - if (index === 0) { - ypos = y + index * (fontSize + padding); - } else { - ypos = y + index * (fontSize + padding * 2); - } - - context.fillText(text, x + padding, y + padding + index * (fontSize + padding)); - }); - - context.restore(); - - // Return the bounding box so it can be used for pointNearHandle - return boundingBox; - } - - // module exports - cornerstoneTools.drawTextBox = drawTextBox; - -})(cornerstoneTools); - -// End Source; src/util/drawTextBox.js - -// Begin Source: src/util/getLuminance.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - function getLuminance(element, x, y, width, height) { - if (!element) { - throw 'getLuminance: parameter element must not be undefined'; - } - - x = Math.round(x); - y = Math.round(y); - var enabledElement = cornerstone.getEnabledElement(element); - var image = enabledElement.image; - var luminance = []; - var index = 0; - var pixelData = image.getPixelData(); - var spIndex, - row, - column; - - if (image.color) { - for (row = 0; row < height; row++) { - for (column = 0; column < width; column++) { - spIndex = (((row + y) * image.columns) + (column + x)) * 4; - var red = pixelData[spIndex]; - var green = pixelData[spIndex + 1]; - var blue = pixelData[spIndex + 2]; - luminance[index++] = 0.2126 * red + 0.7152 * green + 0.0722 * blue; - } - } - } else { - for (row = 0; row < height; row++) { - for (column = 0; column < width; column++) { - spIndex = ((row + y) * image.columns) + (column + x); - luminance[index++] = pixelData[spIndex] * image.slope + image.intercept; - } - } - } - - return luminance; - } - - // module exports - cornerstoneTools.getLuminance = getLuminance; - -})(cornerstone, cornerstoneTools); - -// End Source; src/util/getLuminance.js - -// Begin Source: src/util/getMaxSimultaneousRequests.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - // Maximum concurrent connections to the same server - // Information from http://sgdev-blog.blogspot.fr/2014/01/maximum-concurrent-connection-to-same.html - var maxSimultaneousRequests = { - default: 6, - IE: { - 9: 6, - 10: 8, - default: 8 - }, - Firefox: { - default: 6 - }, - Opera: { - 10: 8, - 11: 6, - 12: 6, - default: 6 - }, - Chrome: { - default: 6 - }, - Safari: { - default: 6 - } - }; - - // Browser name / version detection - // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript - function getBrowserInfo() { - var ua = navigator.userAgent, - M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [], - tem; - - if (/trident/i.test(M[1])) { - tem = /\brv[ :]+(\d+)/g.exec(ua) || []; - return 'IE ' + (tem[1] || ''); - } - - if (M[1] === 'Chrome') { - tem = ua.match(/\b(OPR|Edge)\/(\d+)/); - if (tem !== null) { - return tem.slice(1).join(' ').replace('OPR', 'Opera'); - } - } - - M = M[2]? [ M[1], M[2] ]: [ navigator.appName, navigator.appVersion, '-?' ]; - if ((tem = ua.match(/version\/(\d+)/i)) !== null) { - M.splice(1, 1, tem[1]); - } - - return M.join(' '); - } - - function getMaxSimultaneousRequests() { - var config = cornerstoneTools.stackPrefetch.getConfiguration(); - - // Give preference to user-chosen values - if (config.maxSimultaneousRequests) { - return config.maxSimultaneousRequests; - } - - return getDefaultSimultaneousRequests(); - } - - function getDefaultSimultaneousRequests() { - var infoString = getBrowserInfo(); - var info = infoString.split(' '); - var browserName = info[0]; - var browserVersion = info[1]; - var browserData = maxSimultaneousRequests[browserName]; - - if (!browserData) { - return maxSimultaneousRequests['default']; - } - - if (!browserData[browserVersion]) { - return browserData['default']; - } - - return browserData[browserVersion]; - } - - function isMobileDevice() { - var pattern = new RegExp('Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini'); - return !!pattern.test(navigator.userAgent); - } - - // module exports - cornerstoneTools.getDefaultSimultaneousRequests = getDefaultSimultaneousRequests; - cornerstoneTools.getMaxSimultaneousRequests = getMaxSimultaneousRequests; - cornerstoneTools.getBrowserInfo = getBrowserInfo; - cornerstoneTools.isMobileDevice = isMobileDevice; - -})(cornerstone, cornerstoneTools); - -// End Source; src/util/getMaxSimultaneousRequests.js - -// Begin Source: src/util/getRGBPixels.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - function getRGBPixels(element, x, y, width, height) { - if (!element) { - throw 'getRGBPixels: parameter element must not be undefined'; - } - - x = Math.round(x); - y = Math.round(y); - var enabledElement = cornerstone.getEnabledElement(element); - var storedPixelData = []; - var index = 0; - var pixelData = enabledElement.image.getPixelData(); - var spIndex, - row, - column; - - if (enabledElement.image.color) { - for (row = 0; row < height; row++) { - for (column = 0; column < width; column++) { - spIndex = (((row + y) * enabledElement.image.columns) + (column + x)) * 4; - var red = pixelData[spIndex]; - var green = pixelData[spIndex + 1]; - var blue = pixelData[spIndex + 2]; - var alpha = pixelData[spIndex + 3]; - storedPixelData[index++] = red; - storedPixelData[index++] = green; - storedPixelData[index++] = blue; - storedPixelData[index++] = alpha; - } - } - } - - return storedPixelData; - } - - // module exports - cornerstoneTools.getRGBPixels = getRGBPixels; - -})(cornerstone, cornerstoneTools); - -// End Source; src/util/getRGBPixels.js - -// Begin Source: src/util/isMouseButtonEnabled.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - function isMouseButtonEnabled(which, mouseButtonMask) { - /*jshint bitwise: false*/ - var mouseButton = (1 << (which - 1)); - return ((mouseButtonMask & mouseButton) !== 0); - } - - // module exports - cornerstoneTools.isMouseButtonEnabled = isMouseButtonEnabled; - -})(cornerstone, cornerstoneTools); - -// End Source; src/util/isMouseButtonEnabled.js - -// Begin Source: src/util/pauseEvent.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - /** - * This function is used to prevent selection from occuring when left click dragging on the image - * @param e the event that is provided to your event handler - * Based on: http://stackoverflow.com/questions/5429827/how-can-i-prevent-text-element-selection-with-cursor-drag - * @returns {boolean} - */ - function pauseEvent(e) { - if (e.stopPropagation) { - e.stopPropagation(); - } - - if (e.preventDefault) { - e.preventDefault(); - } - - e.cancelBubble = true; - e.returnValue = false; - return false; - } - - // module exports - cornerstoneTools.pauseEvent = pauseEvent; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/util/pauseEvent.js - -// Begin Source: src/util/pointInEllipse.js -(function(cornerstoneTools) { - - 'use strict'; - - function pointInEllipse(ellipse, location) { - var xRadius = ellipse.width / 2; - var yRadius = ellipse.height / 2; - - if (xRadius <= 0.0 || yRadius <= 0.0) { - return false; - } - - var center = { - x: ellipse.left + xRadius, - y: ellipse.top + yRadius - }; - - /* This is a more general form of the circle equation - * - * X^2/a^2 + Y^2/b^2 <= 1 - */ - - var normalized = { - x: location.x - center.x, - y: location.y - center.y - }; - - var inEllipse = ((normalized.x * normalized.x) / (xRadius * xRadius)) + ((normalized.y * normalized.y) / (yRadius * yRadius)) <= 1.0; - return inEllipse; - } - - cornerstoneTools.pointInEllipse = pointInEllipse; - -})(cornerstoneTools); - -// End Source; src/util/pointInEllipse.js - -// Begin Source: src/util/pointInsideBoundingBox.js -(function(cornerstoneMath, cornerstoneTools) { - - 'use strict'; - - function pointInsideBoundingBox(handle, coords) { - if (!handle.boundingBox) { - return; - } - - return cornerstoneMath.point.insideRect(coords, handle.boundingBox); - } - - // module exports - cornerstoneTools.pointInsideBoundingBox = pointInsideBoundingBox; - -})(cornerstoneMath, cornerstoneTools); - -// End Source; src/util/pointInsideBoundingBox.js - -// Begin Source: src/util/pointProjector.js -(function($, cornerstone, cornerstoneTools) { - - 'use strict'; - - // projects a patient point to an image point - function projectPatientPointToImagePlane(patientPoint, imagePlane) { - var point = patientPoint.clone().sub(imagePlane.imagePositionPatient); - var x = imagePlane.rowCosines.dot(point) / imagePlane.columnPixelSpacing; - var y = imagePlane.columnCosines.dot(point) / imagePlane.rowPixelSpacing; - return { - x: x, - y: y - }; - } - - // projects an image point to a patient point - function imagePointToPatientPoint(imagePoint, imagePlane) { - var x = imagePlane.rowCosines.clone().multiplyScalar(imagePoint.x); - x.multiplyScalar(imagePlane.columnPixelSpacing); - var y = imagePlane.columnCosines.clone().multiplyScalar(imagePoint.y); - y.multiplyScalar(imagePlane.rowPixelSpacing); - var patientPoint = x.add(y); - patientPoint.add(imagePlane.imagePositionPatient); - return patientPoint; - } - - function getRectangleFromImagePlane(imagePlane) { - // Get the points - var topLeft = imagePointToPatientPoint({ - x: 0, - y: 0 - }, imagePlane); - var topRight = imagePointToPatientPoint({ - x: imagePlane.columns, - y: 0 - }, imagePlane); - var bottomLeft = imagePointToPatientPoint({ - x: 0, - y: imagePlane.rows - }, imagePlane); - var bottomRight = imagePointToPatientPoint({ - x: imagePlane.columns, - y: imagePlane.rows - }, imagePlane); - - // Get each side as a vector - var rect = { - top: new cornerstoneMath.Line3(topLeft, topRight), - left: new cornerstoneMath.Line3(topLeft, bottomLeft), - right: new cornerstoneMath.Line3(topRight, bottomRight), - bottom: new cornerstoneMath.Line3(bottomLeft, bottomRight), - }; - return rect; - } - - function lineRectangleIntersection(line, rect) { - var intersections = []; - Object.keys(rect).forEach(function(side) { - var segment = rect[side]; - var intersection = line.intersectLine(segment); - if (intersection) { - intersections.push(intersection); - } - }); - return intersections; - } - - function planePlaneIntersection(targetImagePlane, referenceImagePlane) { - // Gets the line of intersection between two planes in patient space - - // First, get the normals of each image plane - var targetNormal = targetImagePlane.rowCosines.clone().cross(targetImagePlane.columnCosines); - var targetPlane = new cornerstoneMath.Plane(); - targetPlane.setFromNormalAndCoplanarPoint(targetNormal, targetImagePlane.imagePositionPatient); - - var referenceNormal = referenceImagePlane.rowCosines.clone().cross(referenceImagePlane.columnCosines); - var referencePlane = new cornerstoneMath.Plane(); - referencePlane.setFromNormalAndCoplanarPoint(referenceNormal, referenceImagePlane.imagePositionPatient); - - var originDirection = referencePlane.clone().intersectPlane(targetPlane); - var origin = originDirection.origin; - var direction = originDirection.direction; - - // Calculate the longest possible length in the reference image plane (the length of the diagonal) - var bottomRight = imagePointToPatientPoint({ - x: referenceImagePlane.columns, - y: referenceImagePlane.rows - }, referenceImagePlane); - var distance = referenceImagePlane.imagePositionPatient.distanceTo(bottomRight); - - // Use this distance to bound the ray intersecting the two planes - var line = new cornerstoneMath.Line3(); - line.start = origin; - line.end = origin.clone().add(direction.multiplyScalar(distance)); - - // Find the intersections between this line and the reference image plane's four sides - var rect = getRectangleFromImagePlane(referenceImagePlane); - var intersections = lineRectangleIntersection(line, rect); - - // Return the intersections between this line and the reference image plane's sides - // in order to draw the reference line from the target image. - if (intersections.length !== 2) { - return; - } - - var points = { - start: intersections[0], - end: intersections[1] - }; - return points; - - } - - // module/private exports - cornerstoneTools.projectPatientPointToImagePlane = projectPatientPointToImagePlane; - cornerstoneTools.imagePointToPatientPoint = imagePointToPatientPoint; - cornerstoneTools.planePlaneIntersection = planePlaneIntersection; - -})($, cornerstone, cornerstoneTools); - -// End Source; src/util/pointProjector.js - -// Begin Source: src/util/scroll.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - function scroll(element, images) { - var toolData = cornerstoneTools.getToolState(element, 'stack'); - if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) { - return; - } - - var stackData = toolData.data[0]; - - var newImageIdIndex = stackData.currentImageIdIndex + images; - newImageIdIndex = Math.min(stackData.imageIds.length - 1, newImageIdIndex); - newImageIdIndex = Math.max(0, newImageIdIndex); - - cornerstoneTools.scrollToIndex(element, newImageIdIndex); - } - - // module exports - cornerstoneTools.scroll = scroll; - -})(cornerstone, cornerstoneTools); - -// End Source; src/util/scroll.js - -// Begin Source: src/util/scrollToIndex.js -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - function scrollToIndex(element, newImageIdIndex) { - var toolData = cornerstoneTools.getToolState(element, 'stack'); - if (!toolData || !toolData.data || !toolData.data.length) { - return; - } - - var stackData = toolData.data[0]; - - // Allow for negative indexing - if (newImageIdIndex < 0) { - newImageIdIndex += stackData.imageIds.length; - } - - var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); - var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler(); - var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler(); - var viewport = cornerstone.getViewport(element); - - function doneCallback(image) { - if (stackData.currentImageIdIndex !== newImageIdIndex) { - return; - } - - // Check if the element is still enabled in Cornerstone, - // if an error is thrown, stop here. - try { - // TODO: Add 'isElementEnabled' to Cornerstone? - cornerstone.getEnabledElement(element); - } catch(error) { - return; - } - - cornerstone.displayImage(element, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(element, image); - } - } - - function failCallback(error) { - var imageId = stackData.imageIds[newImageIdIndex]; - if (errorLoadingHandler) { - errorLoadingHandler(element, imageId, error); - } - } - - if (newImageIdIndex === stackData.currentImageIdIndex) { - return; - } - - if (startLoadingHandler) { - startLoadingHandler(element); - } - - var eventData = { - newImageIdIndex: newImageIdIndex, - direction: newImageIdIndex - stackData.currentImageIdIndex - }; - - stackData.currentImageIdIndex = newImageIdIndex; - var newImageId = stackData.imageIds[newImageIdIndex]; - - // Retry image loading in cases where previous image promise - // was rejected, if the option is set - var config = cornerstoneTools.stackScroll.getConfiguration(); - if (config && config.retryLoadOnScroll === true) { - var newImagePromise = cornerstone.imageCache.getImagePromise(newImageId); - if (newImagePromise && newImagePromise.state() === 'rejected') { - cornerstone.imageCache.removeImagePromise(newImageId); - } - } - - // Convert the preventCache value in stack data to a boolean - var preventCache = !!stackData.preventCache; - - var imagePromise; - if (preventCache) { - imagePromise = cornerstone.loadImage(newImageId); - } else { - imagePromise = cornerstone.loadAndCacheImage(newImageId); - } - - imagePromise.then(doneCallback, failCallback); - // Make sure we kick off any changed download request pools - cornerstoneTools.requestPoolManager.startGrabbing(); - - $(element).trigger('CornerstoneStackScroll', eventData); - } - - // module exports - cornerstoneTools.scrollToIndex = scrollToIndex; - cornerstoneTools.loadHandlers = {}; - -})(cornerstone, cornerstoneTools); - -// End Source; src/util/scrollToIndex.js - -// Begin Source: src/util/setContextToDisplayFontSize.js -/** - * This module sets the transformation matrix for a canvas context so it displays fonts - * smoothly even when the image is highly scaled up - */ -(function(cornerstone, cornerstoneTools) { - - 'use strict'; - - /** - * Sets the canvas context transformation matrix so it is scaled to show text - * more cleanly even if the image is scaled up. See - * https://github.com/chafey/cornerstoneTools/wiki/DrawingText - * for more information - * - * @param ee - * @param context - * @param fontSize - * @returns {{fontSize: number, lineHeight: number, fontScale: number}} - */ - function setContextToDisplayFontSize(ee, context, fontSize) { - var fontScale = 0.1; - cornerstone.setToPixelCoordinateSystem(ee, context, fontScale); - // return the font size to use - var scaledFontSize = fontSize / ee.viewport.scale / fontScale; - // TODO: actually calculate this? - var lineHeight = fontSize / ee.viewport.scale / fontScale; - return { - fontSize: scaledFontSize, - lineHeight: lineHeight, - fontScale: fontScale - }; - } - - // Module exports - cornerstoneTools.setContextToDisplayFontSize = setContextToDisplayFontSize; - -})(cornerstone, cornerstoneTools); - -// End Source; src/util/setContextToDisplayFontSize.js diff --git a/Packages/ohif-cornerstone/client/cornerstoneWADOImageLoader.js b/Packages/ohif-cornerstone/client/cornerstoneWADOImageLoader.js deleted file mode 100644 index e6f6aae0c..000000000 --- a/Packages/ohif-cornerstone/client/cornerstoneWADOImageLoader.js +++ /dev/null @@ -1,2196 +0,0 @@ -/*! cornerstone-wado-image-loader - v0.14.3 - 2017-04-04 | (c) 2016 Chris Hafey | https://github.com/chafey/cornerstoneWADOImageLoader */ -// -// This is a cornerstone image loader for WADO-URI requests. -// - -if(typeof cornerstone === 'undefined'){ - cornerstone = {}; -} -if(typeof cornerstoneWADOImageLoader === 'undefined'){ - cornerstoneWADOImageLoader = { - wadouri: { - - }, - wadors: { - - }, - internal: { - options : { - // callback allowing customization of the xhr (e.g. adding custom auth headers, cors, etc) - beforeSend: function (xhr) { - }, - // callback allowing modification of newly created image objects - imageCreated : function(image) { - } - } - } - }; -} - - - -(function ($, cornerstone, cornerstoneWADOImageLoader) { - - "use strict"; - - // add a decache callback function to clear out our dataSetCacheManager - function addDecache(image) { - image.decache = function() { - //console.log('decache'); - var parsedImageId = cornerstoneWADOImageLoader.wadouri.parseImageId(image.imageId); - cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.unload(parsedImageId.url); - }; - } - - function getPixelData(dataSet, frameIndex) { - var pixelDataElement = dataSet.elements.x7fe00010; - - if(pixelDataElement.encapsulatedPixelData) { - return cornerstoneWADOImageLoader.wadouri.getEncapsulatedImageFrame(dataSet, frameIndex); - } else { - return cornerstoneWADOImageLoader.wadouri.getUncompressedImageFrame(dataSet, frameIndex); - } - } - - function loadDataSetFromPromise(xhrRequestPromise, imageId, frame, sharedCacheKey, options) { - - var start = new Date().getTime(); - frame = frame || 0; - var deferred = $.Deferred(); - xhrRequestPromise.then(function(dataSet/*, xhr*/) { - var pixelData = getPixelData(dataSet, frame); - var transferSyntax = dataSet.string('x00020010'); - var loadEnd = new Date().getTime(); - var imagePromise = cornerstoneWADOImageLoader.createImage(imageId, pixelData, transferSyntax, options); - imagePromise.then(function(image) { - image.data = dataSet; - var end = new Date().getTime(); - image.loadTimeInMS = loadEnd - start; - image.totalTimeInMS = end - start; - addDecache(image); - deferred.resolve(image); - }); - }, function(error) { - deferred.reject(error); - }); - return deferred; - } - - function getLoaderForScheme(scheme) { - if(scheme === 'dicomweb' || scheme === 'wadouri') { - return cornerstoneWADOImageLoader.internal.xhrRequest; - } - else if(scheme === 'dicomfile') { - return cornerstoneWADOImageLoader.wadouri.loadFileRequest; - } - } - - function loadImage(imageId, options) { - var parsedImageId = cornerstoneWADOImageLoader.wadouri.parseImageId(imageId); - - var loader = getLoaderForScheme(parsedImageId.scheme); - - // if the dataset for this url is already loaded, use it - if(cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.isLoaded(parsedImageId.url)) { - return loadDataSetFromPromise(cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.load(parsedImageId.url, loader, imageId), imageId, parsedImageId.frame, parsedImageId.url, options); - } - - // load the dataSet via the dataSetCacheManager - return loadDataSetFromPromise(cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.load(parsedImageId.url, loader, imageId), imageId, parsedImageId.frame, parsedImageId.url, options); - } - - // register dicomweb and wadouri image loader prefixes - cornerstone.registerImageLoader('dicomweb', loadImage); - cornerstone.registerImageLoader('wadouri', loadImage); - cornerstone.registerImageLoader('dicomfile', loadImage); -}($, cornerstone, cornerstoneWADOImageLoader)); -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function convertPALETTECOLOR( imageFrame, rgbaBuffer ) { - var numPixels = imageFrame.columns * imageFrame.rows; - var palIndex=0; - var rgbaIndex=0; - var pixelData = imageFrame.pixelData; - var start = imageFrame.redPaletteColorLookupTableDescriptor[1]; - var rData = imageFrame.redPaletteColorLookupTableData; - var gData = imageFrame.greenPaletteColorLookupTableData; - var bData = imageFrame.bluePaletteColorLookupTableData; - var shift = imageFrame.redPaletteColorLookupTableDescriptor[2] === 8 ? 0 : 8; - var len = imageFrame.redPaletteColorLookupTableData.length; - if(len === 0) { - len = 65535; - } - - for( var i=0 ; i < numPixels ; ++i ) { - var value=pixelData[palIndex++]; - if( value < start ) - value=0; - else if( value > start + len -1 ) - value=len-1; - else - value=value-start; - - rgbaBuffer[ rgbaIndex++ ] = rData[value] >> shift; - rgbaBuffer[ rgbaIndex++ ] = gData[value] >> shift; - rgbaBuffer[ rgbaIndex++ ] = bData[value] >> shift; - rgbaBuffer[ rgbaIndex++ ] = 255; - } - } - - // module exports - cornerstoneWADOImageLoader.convertPALETTECOLOR = convertPALETTECOLOR; - -}(cornerstoneWADOImageLoader)); -/** - */ -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function convertRGBColorByPixel(imageFrame, rgbaBuffer) { - if(imageFrame === undefined) { - throw "decodeRGB: rgbBuffer must not be undefined"; - } - if(imageFrame.length % 3 !== 0) { - throw "decodeRGB: rgbBuffer length must be divisible by 3"; - } - - var numPixels = imageFrame.length / 3; - var rgbIndex = 0; - var rgbaIndex = 0; - for(var i= 0; i < numPixels; i++) { - rgbaBuffer[rgbaIndex++] = imageFrame[rgbIndex++]; // red - rgbaBuffer[rgbaIndex++] = imageFrame[rgbIndex++]; // green - rgbaBuffer[rgbaIndex++] = imageFrame[rgbIndex++]; // blue - rgbaBuffer[rgbaIndex++] = 255; //alpha - } - } - - // module exports - cornerstoneWADOImageLoader.convertRGBColorByPixel = convertRGBColorByPixel; -}(cornerstoneWADOImageLoader)); -/** - */ -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function convertRGBColorByPlane(imageFrame, rgbaBuffer) { - if(imageFrame === undefined) { - throw "decodeRGB: rgbBuffer must not be undefined"; - } - if(imageFrame.length % 3 !== 0) { - throw "decodeRGB: rgbBuffer length must be divisible by 3"; - } - - var numPixels = imageFrame.length / 3; - var rgbaIndex = 0; - var rIndex = 0; - var gIndex = numPixels; - var bIndex = numPixels*2; - for(var i= 0; i < numPixels; i++) { - rgbaBuffer[rgbaIndex++] = imageFrame[rIndex++]; // red - rgbaBuffer[rgbaIndex++] = imageFrame[gIndex++]; // green - rgbaBuffer[rgbaIndex++] = imageFrame[bIndex++]; // blue - rgbaBuffer[rgbaIndex++] = 255; //alpha - } - } - - // module exports - cornerstoneWADOImageLoader.convertRGBColorByPlane = convertRGBColorByPlane; -}(cornerstoneWADOImageLoader)); -/** - */ -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function convertYBRFullByPixel(imageFrame, rgbaBuffer) { - if(imageFrame === undefined) { - throw "decodeRGB: ybrBuffer must not be undefined"; - } - if(imageFrame.length % 3 !== 0) { - throw "decodeRGB: ybrBuffer length must be divisble by 3"; - } - - var numPixels = imageFrame.length / 3; - var ybrIndex = 0; - var rgbaIndex = 0; - for(var i= 0; i < numPixels; i++) { - var y = imageFrame[ybrIndex++]; - var cb = imageFrame[ybrIndex++]; - var cr = imageFrame[ybrIndex++]; - rgbaBuffer[rgbaIndex++] = y + 1.40200 * (cr - 128);// red - rgbaBuffer[rgbaIndex++] = y - 0.34414 * (cb -128) - 0.71414 * (cr- 128); // green - rgbaBuffer[rgbaIndex++] = y + 1.77200 * (cb - 128); // blue - rgbaBuffer[rgbaIndex++] = 255; //alpha - } - } - - // module exports - cornerstoneWADOImageLoader.convertYBRFullByPixel = convertYBRFullByPixel; -}(cornerstoneWADOImageLoader)); -/** - */ -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function convertYBRFullByPlane(imageFrame, rgbaBuffer) { - if (imageFrame === undefined) { - throw "decodeRGB: ybrBuffer must not be undefined"; - } - if (imageFrame.length % 3 !== 0) { - throw "decodeRGB: ybrBuffer length must be divisble by 3"; - } - - - var numPixels = imageFrame.length / 3; - var rgbaIndex = 0; - var yIndex = 0; - var cbIndex = numPixels; - var crIndex = numPixels * 2; - for (var i = 0; i < numPixels; i++) { - var y = imageFrame[yIndex++]; - var cb = imageFrame[cbIndex++]; - var cr = imageFrame[crIndex++]; - rgbaBuffer[rgbaIndex++] = y + 1.40200 * (cr - 128);// red - rgbaBuffer[rgbaIndex++] = y - 0.34414 * (cb - 128) - 0.71414 * (cr - 128); // green - rgbaBuffer[rgbaIndex++] = y + 1.77200 * (cb - 128); // blue - rgbaBuffer[rgbaIndex++] = 255; //alpha - } - } - // module exports - cornerstoneWADOImageLoader.convertYBRFullByPlane = convertYBRFullByPlane; -}(cornerstoneWADOImageLoader)); -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function configure(options) { - cornerstoneWADOImageLoader.internal.options = options; - } - - // module exports - cornerstoneWADOImageLoader.configure = configure; - -}(cornerstoneWADOImageLoader)); -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function convertRGB(imageFrame, rgbaBuffer) { - if(imageFrame.planarConfiguration === 0) { - cornerstoneWADOImageLoader.convertRGBColorByPixel(imageFrame.pixelData, rgbaBuffer); - } else { - cornerstoneWADOImageLoader.convertRGBColorByPlane(imageFrame.pixelData, rgbaBuffer); - } - } - - function convertYBRFull(imageFrame, rgbaBuffer) { - if(imageFrame.planarConfiguration === 0) { - cornerstoneWADOImageLoader.convertYBRFullByPixel(imageFrame.pixelData, rgbaBuffer); - } else { - cornerstoneWADOImageLoader.convertYBRFullByPlane(imageFrame.pixelData, rgbaBuffer); - } - } - - function convertColorSpace(imageFrame, imageData) { - var rgbaBuffer = imageData.data; - console.time('convertColorSpace'); - // convert based on the photometric interpretation - if (imageFrame.photometricInterpretation === "RGB" ) - { - convertRGB(imageFrame, rgbaBuffer); - } - else if (imageFrame.photometricInterpretation === "YBR_RCT") - { - convertRGB(imageFrame, rgbaBuffer); - } - else if (imageFrame.photometricInterpretation === "YBR_ICT") - { - convertRGB(imageFrame, rgbaBuffer); - } - else if( imageFrame.photometricInterpretation === "PALETTE COLOR" ) - { - cornerstoneWADOImageLoader.convertPALETTECOLOR(imageFrame, rgbaBuffer); - } - else if( imageFrame.photometricInterpretation === "YBR_FULL_422" ) - { - convertRGB(imageFrame, rgbaBuffer); - } - else if(imageFrame.photometricInterpretation === "YBR_FULL" ) - { - convertYBRFull(imageFrame, rgbaBuffer); - } - else - { - throw "no color space conversion for photometric interpretation " + imageFrame.photometricInterpretation; - } - console.timeEnd('convertColorSpace'); - } - - // module exports - cornerstoneWADOImageLoader.convertColorSpace = convertColorSpace; - -}(cornerstoneWADOImageLoader)); -/** - */ -(function ($, cornerstone, cornerstoneWADOImageLoader) { - - "use strict"; - - var canvas = document.createElement('canvas'); - var lastImageIdDrawn = ""; - - function isModalityLUTForDisplay(sopClassUid) { - // special case for XA and XRF - // https://groups.google.com/forum/#!searchin/comp.protocols.dicom/Modality$20LUT$20XA/comp.protocols.dicom/UBxhOZ2anJ0/D0R_QP8V2wIJ - return sopClassUid !== '1.2.840.10008.5.1.4.1.1.12.1' && // XA - sopClassUid !== '1.2.840.10008.5.1.4.1.1.12.2.1 '; // XRF - } - - /** - * Helper function to set pixel data to the right typed array. This is needed because web workers - * can transfer array buffers but not typed arrays - * @param imageFrame - */ - function setPixelDataType(imageFrame) { - if(imageFrame.bitsAllocated === 16) { - if(imageFrame.pixelRepresentation === 0) { - imageFrame.pixelData = new Uint16Array(imageFrame.pixelData); - } else { - imageFrame.pixelData = new Int16Array(imageFrame.pixelData); - } - } else { - imageFrame.pixelData = new Uint8Array(imageFrame.pixelData); - } - } - - function createImage(imageId, pixelData, transferSyntax, options) { - var deferred = $.Deferred(); - var imageFrame = cornerstoneWADOImageLoader.getImageFrame(imageId); - var decodePromise = cornerstoneWADOImageLoader.decodeImageFrame(imageFrame, transferSyntax, pixelData, canvas, options); - decodePromise.then(function(imageFrame) { - //var imagePixelModule = metaDataProvider('imagePixelModule', imageId); - var imagePlaneModule = cornerstone.metaData.get('imagePlaneModule', imageId); - var voiLutModule = cornerstone.metaData.get('voiLutModule', imageId); - var modalityLutModule = cornerstone.metaData.get('modalityLutModule', imageId); - var sopCommonModule = cornerstone.metaData.get('sopCommonModule', imageId); - var isColorImage = cornerstoneWADOImageLoader.isColorImage(imageFrame.photometricInterpretation); - - // JPEGBaseline (8 bits) is already returning the pixel data in the right format (rgba) - // because it's using a canvas to load and decode images. - if(!cornerstoneWADOImageLoader.isJPEGBaseline8BitColor(imageFrame, transferSyntax)) { - setPixelDataType(imageFrame); - - // convert color space - if(isColorImage) { - // setup the canvas context - canvas.height = imageFrame.rows; - canvas.width = imageFrame.columns; - - var context = canvas.getContext('2d'); - var imageData = context.createImageData(imageFrame.columns, imageFrame.rows); - cornerstoneWADOImageLoader.convertColorSpace(imageFrame, imageData); - imageFrame.imageData = imageData; - imageFrame.pixelData = imageData.data; - } - } - - var image = { - imageId: imageId, - color: isColorImage, - columnPixelSpacing: imagePlaneModule.pixelSpacing ? imagePlaneModule.pixelSpacing[1] : undefined, - columns: imageFrame.columns, - height: imageFrame.rows, - intercept: modalityLutModule.rescaleIntercept ? modalityLutModule.rescaleIntercept: 0, - invert: imageFrame.photometricInterpretation === "MONOCHROME1", - minPixelValue : imageFrame.smallestPixelValue, - maxPixelValue : imageFrame.largestPixelValue, - render: undefined, // set below - rowPixelSpacing: imagePlaneModule.pixelSpacing ? imagePlaneModule.pixelSpacing[0] : undefined, - rows: imageFrame.rows, - sizeInBytes: imageFrame.pixelData.length, - slope: modalityLutModule.rescaleSlope ? modalityLutModule.rescaleSlope: 1, - width: imageFrame.columns, - windowCenter: voiLutModule.windowCenter ? voiLutModule.windowCenter[0] : undefined, - windowWidth: voiLutModule.windowWidth ? voiLutModule.windowWidth[0] : undefined, - decodeTimeInMS : imageFrame.decodeTimeInMS, - webWorkerTimeInMS: imageFrame.webWorkerTimeInMS - }; - - // add function to return pixel data - image.getPixelData = function() { - return imageFrame.pixelData; - }; - - // Setup the renderer - if(image.color) { - image.render = cornerstone.renderColorImage; - image.getCanvas = function() { - if(lastImageIdDrawn === imageId) { - return canvas; - } - - canvas.height = image.rows; - canvas.width = image.columns; - var context = canvas.getContext('2d'); - context.putImageData(imageFrame.imageData, 0, 0 ); - lastImageIdDrawn = imageId; - return canvas; - }; - - } else { - image.render = cornerstone.renderGrayscaleImage; - } - - // calculate min/max if not supplied - if(image.minPixelValue === undefined || image.maxPixelValue === undefined) { - var minMax = cornerstoneWADOImageLoader.getMinMax(imageFrame.pixelData); - image.minPixelValue = minMax.min; - image.maxPixelValue = minMax.max; - } - - // Modality LUT - if(modalityLutModule.modalityLUTSequence && - modalityLutModule.modalityLUTSequence.length > 0 && - isModalityLUTForDisplay(sopCommonModule.sopClassUID)) { - image.modalityLUT = modalityLutModule.modalityLUTSequence[0]; - } - - // VOI LUT - if(voiLutModule.voiLUTSequence && - voiLutModule.voiLUTSequence.length > 0) { - image.voiLUT = voiLutModule.voiLUTSequence[0]; - } - - // set the ww/wc to cover the dynamic range of the image if no values are supplied - if(image.windowCenter === undefined || image.windowWidth === undefined) { - if(image.color) { - image.windowWidth = 255; - image.windowCenter = 128; - } else { - var maxVoi = image.maxPixelValue * image.slope + image.intercept; - var minVoi = image.minPixelValue * image.slope + image.intercept; - image.windowWidth = maxVoi - minVoi; - image.windowCenter = (maxVoi + minVoi) / 2; - } - } - deferred.resolve(image); - }); - return deferred.promise(); - } - - cornerstoneWADOImageLoader.createImage = createImage; -}($, cornerstone, cornerstoneWADOImageLoader)); -/** - */ -(function ($, cornerstone, cornerstoneWADOImageLoader) { - - "use strict"; - - function addDecodeTask(imageFrame, transferSyntax, pixelData, options) { - var priority = options.priority || undefined; - var transferList = options.transferPixelData ? [pixelData.buffer] : undefined; - - return cornerstoneWADOImageLoader.webWorkerManager.addTask( - 'decodeTask', - { - imageFrame : imageFrame, - transferSyntax : transferSyntax, - pixelData : pixelData, - options: options - }, priority, transferList).promise; - } - - function decodeImageFrame(imageFrame, transferSyntax, pixelData, canvas, options) { - options = options || {}; - - // Implicit VR Little Endian - if(transferSyntax === "1.2.840.10008.1.2") { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // Explicit VR Little Endian - else if(transferSyntax === "1.2.840.10008.1.2.1") { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // Explicit VR Big Endian (retired) - else if (transferSyntax === "1.2.840.10008.1.2.2" ) { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // Deflate transfer syntax (deflated by dicomParser) - else if(transferSyntax === '1.2.840.10008.1.2.1.99') { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // RLE Lossless - else if (transferSyntax === "1.2.840.10008.1.2.5" ) - { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // JPEG Baseline lossy process 1 (8 bit) - else if (transferSyntax === "1.2.840.10008.1.2.4.50") - { - // Handle 8-bit JPEG Baseline color images using the browser's built-in - // JPEG decoding - if(imageFrame.bitsAllocated === 8 && - (imageFrame.samplesPerPixel === 3 || imageFrame.samplesPerPixel === 4)) - { - return cornerstoneWADOImageLoader.decodeJPEGBaseline8BitColor(imageFrame, pixelData, canvas); - } else { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - } - // JPEG Baseline lossy process 2 & 4 (12 bit) - else if (transferSyntax === "1.2.840.10008.1.2.4.51") - { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // JPEG Lossless, Nonhierarchical (Processes 14) - else if (transferSyntax === "1.2.840.10008.1.2.4.57") - { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // JPEG Lossless, Nonhierarchical (Processes 14 [Selection 1]) - else if (transferSyntax === "1.2.840.10008.1.2.4.70" ) - { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // JPEG-LS Lossless Image Compression - else if (transferSyntax === "1.2.840.10008.1.2.4.80" ) - { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // JPEG-LS Lossy (Near-Lossless) Image Compression - else if (transferSyntax === "1.2.840.10008.1.2.4.81" ) - { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // JPEG 2000 Lossless - else if (transferSyntax === "1.2.840.10008.1.2.4.90") - { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - // JPEG 2000 Lossy - else if (transferSyntax === "1.2.840.10008.1.2.4.91") - { - return addDecodeTask(imageFrame, transferSyntax, pixelData, options); - } - /* Don't know if these work... - // JPEG 2000 Part 2 Multicomponent Image Compression (Lossless Only) - else if(transferSyntax === "1.2.840.10008.1.2.4.92") - { - return cornerstoneWADOImageLoader.decodeJPEG2000(dataSet, frame); - } - // JPEG 2000 Part 2 Multicomponent Image Compression - else if(transferSyntax === "1.2.840.10008.1.2.4.93") - { - return cornerstoneWADOImageLoader.decodeJPEG2000(dataSet, frame); - } - */ - else - { - if(console && console.log) { - console.log("Image cannot be decoded due to Unsupported transfer syntax " + transferSyntax); - } - throw "no decoder for transfer syntax " + transferSyntax; - } - } - - cornerstoneWADOImageLoader.decodeImageFrame = decodeImageFrame; -}($, cornerstone, cornerstoneWADOImageLoader)); -/** - * Special decoder for 8 bit jpeg that leverages the browser's built in JPEG decoder for increased performance - */ -(function ($, cornerstoneWADOImageLoader) { - - "use strict"; - - function arrayBufferToString(buffer) { - return binaryToString(String.fromCharCode.apply(null, Array.prototype.slice.apply(new Uint8Array(buffer)))); - } - - function binaryToString(binary) { - var error; - - try { - return decodeURIComponent(escape(binary)); - } catch (_error) { - error = _error; - if (error instanceof URIError) { - return binary; - } else { - throw error; - } - } - } - - function decodeJPEGBaseline8BitColor(imageFrame, pixelData, canvas) { - var start = new Date().getTime(); - var deferred = $.Deferred(); - - var imgBlob = new Blob([pixelData], {type: "image/jpeg"}); - - var r = new FileReader(); - if(r.readAsBinaryString === undefined) { - r.readAsArrayBuffer(imgBlob); - } - else { - r.readAsBinaryString(imgBlob); // doesn't work on IE11 - } - - r.onload = function(){ - var img=new Image(); - img.onload = function() { - canvas.height = img.height; - canvas.width = img.width; - imageFrame.rows = img.height; - imageFrame.columns = img.width; - var context = canvas.getContext('2d'); - context.drawImage(this, 0, 0); - var imageData = context.getImageData(0, 0, img.width, img.height); - var end = new Date().getTime(); - imageFrame.pixelData = imageData.data; - imageFrame.imageData = imageData; - imageFrame.decodeTimeInMS = end - start; - deferred.resolve(imageFrame); - }; - img.onerror = function(error) { - deferred.reject(error); - }; - if(r.readAsBinaryString === undefined) { - img.src = "data:image/jpeg;base64,"+window.btoa(arrayBufferToString(r.result)); - } - else { - img.src = "data:image/jpeg;base64,"+window.btoa(r.result); // doesn't work on IE11 - } - - }; - return deferred.promise(); - } - - function isJPEGBaseline8BitColor(imageFrame, transferSyntax) { - transferSyntax = transferSyntax || imageFrame.transferSyntax; - - if(imageFrame.bitsAllocated === 8 && - transferSyntax === "1.2.840.10008.1.2.4.50" && - (imageFrame.samplesPerPixel === 3 || imageFrame.samplesPerPixel === 4)) { - return true; - } - - } - - // module exports - cornerstoneWADOImageLoader.decodeJPEGBaseline8BitColor = decodeJPEGBaseline8BitColor; - cornerstoneWADOImageLoader.isJPEGBaseline8BitColor = isJPEGBaseline8BitColor; - -}($, cornerstoneWADOImageLoader)); -/** - */ -(function ($, cornerstone, cornerstoneWADOImageLoader) { - - "use strict"; - - function getImageFrame(imageId) { - var imagePixelModule = cornerstone.metaData.get('imagePixelModule', imageId); - - return { - samplesPerPixel : imagePixelModule.samplesPerPixel, - photometricInterpretation : imagePixelModule.photometricInterpretation, - planarConfiguration : imagePixelModule.planarConfiguration, - rows : imagePixelModule.rows, - columns : imagePixelModule.columns, - bitsAllocated : imagePixelModule.bitsAllocated, - pixelRepresentation : imagePixelModule.pixelRepresentation, // 0 = unsigned, - smallestPixelValue: imagePixelModule.smallestPixelValue, - largestPixelValue: imagePixelModule.largestPixelValue, - redPaletteColorLookupTableDescriptor : imagePixelModule.redPaletteColorLookupTableDescriptor, - greenPaletteColorLookupTableDescriptor : imagePixelModule.greenPaletteColorLookupTableDescriptor, - bluePaletteColorLookupTableDescriptor : imagePixelModule.bluePaletteColorLookupTableDescriptor, - redPaletteColorLookupTableData : imagePixelModule.redPaletteColorLookupTableData, - greenPaletteColorLookupTableData : imagePixelModule.greenPaletteColorLookupTableData, - bluePaletteColorLookupTableData : imagePixelModule.bluePaletteColorLookupTableData, - pixelData: undefined // populated later after decoding - }; - } - - cornerstoneWADOImageLoader.getImageFrame = getImageFrame; -}($, cornerstone, cornerstoneWADOImageLoader)); -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function getMinMax(storedPixelData) - { - // we always calculate the min max values since they are not always - // present in DICOM and we don't want to trust them anyway as cornerstone - // depends on us providing reliable values for these - var min = storedPixelData[0]; - var max = storedPixelData[0]; - var storedPixel; - var numPixels = storedPixelData.length; - for(var index = 0; index < numPixels; index++) { - storedPixel = storedPixelData[index]; - min = Math.min(min, storedPixel); - max = Math.max(max, storedPixel); - } - - return { - min: min, - max: max - }; - } - - // module exports - cornerstoneWADOImageLoader.getMinMax = getMinMax; - -}(cornerstoneWADOImageLoader)); - - -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function isColorImage(photoMetricInterpretation) - { - if(photoMetricInterpretation === "RGB" || - photoMetricInterpretation === "PALETTE COLOR" || - photoMetricInterpretation === "YBR_FULL" || - photoMetricInterpretation === "YBR_FULL_422" || - photoMetricInterpretation === "YBR_PARTIAL_422" || - photoMetricInterpretation === "YBR_PARTIAL_420" || - photoMetricInterpretation === "YBR_RCT" || - photoMetricInterpretation === "YBR_ICT") - { - return true; - } - else - { - return false; - } - } - - cornerstoneWADOImageLoader.isColorImage = isColorImage; - -}(cornerstoneWADOImageLoader)); -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - // module exports - cornerstoneWADOImageLoader.version = '0.13.3'; - -}(cornerstoneWADOImageLoader)); -(function (cornerstoneWADOImageLoader) { - - function checkToken(token, data, dataOffset) { - - if(dataOffset + token.length > data.length) { - return false; - } - - var endIndex = dataOffset; - - for(var i = 0; i < token.length; i++) { - if(token[i] !== data[endIndex++]) { - return false; - } - } - return true; - } - - function stringToUint8Array(str) { - var uint=new Uint8Array(str.length); - for(var i=0,j=str.length;i= dataSet.byteArray.length) { - throw 'frame exceeds size of pixelData'; - } - return new Uint8Array(dataSet.byteArray.buffer, frameOffset, pixelsPerFrame); - } - else if(bitsAllocated === 16) { - frameOffset = pixelDataOffset + frameIndex * pixelsPerFrame * 2; - if(frameOffset >= dataSet.byteArray.length) { - throw 'frame exceeds size of pixelData'; - } - return new Uint8Array(dataSet.byteArray.buffer, frameOffset,pixelsPerFrame * 2); - } else if (bitsAllocated === 1) { - frameOffset = pixelDataOffset + frameIndex * pixelsPerFrame * 0.125; - if(frameOffset >= dataSet.byteArray.length) { - throw 'frame exceeds size of pixelData'; - } - return cornerstoneWADOImageLoader.wadouri.unpackBinaryFrame(dataSet.byteArray, frameOffset, pixelsPerFrame); - } - - throw 'unsupported pixel format'; - } - - cornerstoneWADOImageLoader.wadouri.getUncompressedImageFrame = getUncompressedImageFrame; -}($, cornerstone, cornerstoneWADOImageLoader)); -(function ($, cornerstone, cornerstoneWADOImageLoader) { - - "use strict"; - - function loadFileRequest(uri) { - - var parsedImageId = cornerstoneWADOImageLoader.wadouri.parseImageId(uri); - var fileIndex = parseInt(parsedImageId.url); - var file = cornerstoneWADOImageLoader.wadouri.fileManager.get(fileIndex); - - // create a deferred object - var deferred = $.Deferred(); - - var fileReader = new FileReader(); - fileReader.onload = function (e) { - var dicomPart10AsArrayBuffer = e.target.result; - deferred.resolve(dicomPart10AsArrayBuffer); - }; - fileReader.readAsArrayBuffer(file); - - return deferred.promise(); - } - cornerstoneWADOImageLoader.wadouri.loadFileRequest = loadFileRequest; -}($, cornerstone, cornerstoneWADOImageLoader)); - -/** - */ -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function getLutDescriptor(dataSet, tag) { - if(!dataSet.elements[tag] || dataSet.elements[tag].length != 6) { - return; - } - return [dataSet.uint16(tag, 0),dataSet.uint16(tag, 1), dataSet.uint16(tag, 2)] - } - - function getLutData(lutDataSet, tag, lutDescriptor) { - var lut = []; - var lutData = lutDataSet.elements[tag]; - var numLutEntries = lutDescriptor[0]; - for (var i = 0; i < numLutEntries; i++) { - // Output range is always unsigned - if(lutDescriptor[2] === 16) { - lut[i] = lutDataSet.uint16(tag, i); - } - else { - lut[i] = lutDataSet.byteArray[i + lutData.dataOffset]; - } - } - return lut; - } - - function populatePaletteColorLut(dataSet, imagePixelModule) { - // return immediately if no palette lut elements - if(!dataSet.elements['x00281101']) { - return; - } - imagePixelModule.redPaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281101'); - imagePixelModule.greenPaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281102'); - imagePixelModule.bluePaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281103'); - - imagePixelModule.redPaletteColorLookupTableData = getLutData(dataSet, 'x00281201', imagePixelModule.redPaletteColorLookupTableDescriptor); - imagePixelModule.greenPaletteColorLookupTableData = getLutData(dataSet, 'x00281202', imagePixelModule.greenPaletteColorLookupTableDescriptor); - imagePixelModule.bluePaletteColorLookupTableData = getLutData(dataSet, 'x00281203', imagePixelModule.bluePaletteColorLookupTableDescriptor); - } - - function populateSmallestLargestPixelValues(dataSet, imagePixelModule) { - var pixelRepresentation = dataSet.uint16('x00280103'); - if(pixelRepresentation === 0) { - imagePixelModule.smallestPixelValue = dataSet.uint16('x00280106'); - imagePixelModule.largestPixelValue = dataSet.uint16('x00280107'); - } else { - imagePixelModule.smallestPixelValue = dataSet.int16('x00280106'); - imagePixelModule.largestPixelValue = dataSet.int16('x00280107'); - } - } - - function getImagePixelModule(dataSet) { - - var imagePixelModule = { - samplesPerPixel: dataSet.uint16('x00280002'), - photometricInterpretation: dataSet.string('x00280004'), - rows: dataSet.uint16('x00280010'), - columns: dataSet.uint16('x00280011'), - bitsAllocated: dataSet.uint16('x00280100'), - bitsStored: dataSet.uint16('x00280101'), - highBit: dataSet.uint16('x00280102'), - pixelRepresentation: dataSet.uint16('x00280103'), - planarConfiguration: dataSet.uint16('x00280006'), - pixelAspectRatio: dataSet.string('x00280034') - }; - populateSmallestLargestPixelValues(dataSet, imagePixelModule); - populatePaletteColorLut(dataSet, imagePixelModule); - return imagePixelModule; - - } - - // module exports - cornerstoneWADOImageLoader.wadouri.getImagePixelModule = getImagePixelModule - -}(cornerstoneWADOImageLoader)); -/** - */ -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function getLUT(pixelRepresentation, lutDataSet) { - var numLUTEntries = lutDataSet.uint16('x00283002', 0); - if(numLUTEntries === 0) { - numLUTEntries = 65535; - } - var firstValueMapped = 0; - if(pixelRepresentation === 0) { - firstValueMapped = lutDataSet.uint16('x00283002', 1); - } else { - firstValueMapped = lutDataSet.int16('x00283002', 1); - } - var numBitsPerEntry = lutDataSet.uint16('x00283002', 2); - //console.log('LUT(', numLUTEntries, ',', firstValueMapped, ',', numBitsPerEntry, ')'); - var lut = { - id : '1', - firstValueMapped: firstValueMapped, - numBitsPerEntry : numBitsPerEntry, - lut : [] - }; - - //console.log("minValue=", minValue, "; maxValue=", maxValue); - for (var i = 0; i < numLUTEntries; i++) { - if(pixelRepresentation === 0) { - lut.lut[i] = lutDataSet.uint16('x00283006', i); - } else { - lut.lut[i] = lutDataSet.int16('x00283006', i); - } - } - return lut; - } - - - function getLUTs(pixelRepresentation, lutSequence) { - if(!lutSequence || !lutSequence.items.length) { - return; - } - var luts = []; - for(var i=0; i < lutSequence.items.length; i++) { - var lutDataSet = lutSequence.items[i].dataSet; - var lut = getLUT(pixelRepresentation, lutDataSet); - if(lut) { - luts.push(lut); - } - } - return luts; - } - - - // module exports - cornerstoneWADOImageLoader.wadouri.getLUTs = getLUTs - -}(cornerstoneWADOImageLoader)); -/** - */ -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function getMinStoredPixelValue(dataSet) { - var pixelRepresentation = dataSet.uint16('x00280103'); - var bitsStored = dataSet.uint16('x00280101'); - if(pixelRepresentation === 0) { - return 0; - } - return -1 << (bitsStored -1); - } - - // 0 = unsigned / US, 1 = signed / SS - function getModalityLUTOutputPixelRepresentation(dataSet) { - - // CT SOP Classes are always signed - var sopClassUID = dataSet.string('x00080016'); - if(sopClassUID === '1.2.840.10008.5.1.4.1.1.2' || - sopClassUID === '1.2.840.10008.5.1.4.1.1.2.1') { - return 1; - } - - // if rescale intercept and rescale slope are present, pass the minimum stored - // pixel value through them to see if we get a signed output range - var rescaleIntercept = dataSet.floatString('x00281052'); - var rescaleSlope = dataSet.floatString('x00281053'); - if(rescaleIntercept !== undefined && rescaleSlope !== undefined) { - var minStoredPixelValue = getMinStoredPixelValue(dataSet); // - var minModalityLutValue = minStoredPixelValue * rescaleSlope + rescaleIntercept; - if (minModalityLutValue < 0) { - return 1; - } else { - return 0; - } - } - - // Output of non linear modality lut is always unsigned - if(dataSet.elements.x00283000 && dataSet.elements.x00283000.length > 0) { - return 0; - } - - // If no modality lut transform, output is same as pixel representation - return dataSet.uint16('x00280103'); - } - - // module exports - cornerstoneWADOImageLoader.wadouri.getModalityLUTOutputPixelRepresentation = getModalityLUTOutputPixelRepresentation - -}(cornerstoneWADOImageLoader)); -/** - */ -(function (cornerstoneWADOImageLoader) { - - "use strict"; - - function getNumberValues(dataSet, tag, minimumLength) { - var values = []; - var valueAsString = dataSet.string(tag); - if(!valueAsString) { - return; - } - var split = valueAsString.split('\\'); - if(minimumLength && split.length < minimumLength) { - return; - } - for(var i=0;i < split.length; i++) { - values.push(parseFloat(split[i])); - } - return values; - } - - // module exports - cornerstoneWADOImageLoader.wadouri.getNumberValues = getNumberValues - -}(cornerstoneWADOImageLoader)); -/** - */ -(function (cornerstone, cornerstoneWADOImageLoader) { - - "use strict"; - - var getNumberValues = cornerstoneWADOImageLoader.wadouri.getNumberValues; - - function metaDataProvider(type, imageId) { - var parsedImageId = cornerstoneWADOImageLoader.wadouri.parseImageId(imageId); - - var dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(parsedImageId.url); - if(!dataSet) { - return; - } - - if(type === 'generalSeriesModule') { - return { - modality: dataSet.string('x00080060'), - seriesInstanceUID: dataSet.string('x0020000e'), - seriesNumber: dataSet.intString('x00200011'), - studyInstanceUID: dataSet.string('x0020000d'), - seriesDate: dicomParser.parseDA(dataSet.string('x00080021')), - seriesTime: dicomParser.parseTM(dataSet.string('x00080031') || '') - }; - } - - if(type === 'patientStudyModule') { - return { - patientAge: dataSet.intString('x00101010'), - patientSize: dataSet.floatString('x00101020'), - patientWeight: dataSet.floatString('x00101030') - } - } - - if (type === 'imagePlaneModule') { - return { - pixelSpacing: getNumberValues(dataSet, 'x00280030', 2), - imageOrientationPatient: getNumberValues(dataSet, 'x00200037', 6), - imagePositionPatient: getNumberValues(dataSet, 'x00200032', 3), - sliceThickness: dataSet.floatString('x00180050'), - sliceLocation: dataSet.floatString('x00201041'), - frameOfReferenceUID: dataSet.string('x00200052') - }; - } - - if (type === 'imagePixelModule') { - return cornerstoneWADOImageLoader.wadouri.getImagePixelModule(dataSet); - } - - if (type === 'modalityLutModule') { - return { - rescaleIntercept : dataSet.floatString('x00281052'), - rescaleSlope : dataSet.floatString('x00281053'), - rescaleType: dataSet.string('x00281054'), - modalityLUTSequence : cornerstoneWADOImageLoader.wadouri.getLUTs(dataSet.uint16('x00280103'), dataSet.elements.x00283000) - }; - } - - if (type === 'voiLutModule') { - var modalityLUTOutputPixelRepresentation = cornerstoneWADOImageLoader.wadouri.getModalityLUTOutputPixelRepresentation(dataSet); - return { - windowCenter : getNumberValues(dataSet, 'x00281050', 1), - windowWidth : getNumberValues(dataSet, 'x00281051', 1), - voiLUTSequence : cornerstoneWADOImageLoader.wadouri.getLUTs(modalityLUTOutputPixelRepresentation, dataSet.elements.x00283010) - }; - } - - if (type === 'sopCommonModule') { - return { - sopClassUID : dataSet.string('x00080016'), - sopInstanceUID : dataSet.string('x00080018') - }; - } - - if (type === 'petIsotopeModule') { - var radiopharmaceuticalInfo = dataSet.elements.x00540016; - if (radiopharmaceuticalInfo === undefined) { - return; - } - - var firstRadiopharmaceuticalInfoDataSet = radiopharmaceuticalInfo.items[0].dataSet; - return { - radiopharmaceuticalInfo: { - radiopharmaceuticalStartTime: dicomParser.parseTM(firstRadiopharmaceuticalInfoDataSet.string('x00181072') || ''), - radionuclideTotalDose: firstRadiopharmaceuticalInfoDataSet.floatString('x00181074'), - radionuclideHalfLife: firstRadiopharmaceuticalInfoDataSet.floatString('x00181075') - } - } - } - - } - - - // register our metadata provider - cornerstone.metaData.addProvider(metaDataProvider); - - // module exports - cornerstoneWADOImageLoader.wadouri.metaDataProvider = metaDataProvider - -}(cornerstone, cornerstoneWADOImageLoader)); -(function (cornerstoneWADOImageLoader) { - - "use strict"; - function parseImageId(imageId) { - // build a url by parsing out the url scheme and frame index from the imageId - var firstColonIndex = imageId.indexOf(':'); - var url = imageId.substring(firstColonIndex + 1); - var frameIndex = url.indexOf('frame='); - var frame; - if(frameIndex !== -1) { - var frameStr = url.substr(frameIndex + 6); - frame = parseInt(frameStr); - url = url.substr(0, frameIndex-1); - } - return { - scheme: imageId.substr(0, firstColonIndex), - url : url, - frame: frame - }; - } - - // module exports - cornerstoneWADOImageLoader.wadouri.parseImageId = parseImageId; - -}(cornerstoneWADOImageLoader)); -/** - * Function to deal with unpacking a binary frame - */ -(function ($, cornerstone, cornerstoneWADOImageLoader) { - - "use strict"; - - function isBitSet(byte, bitPos) { - return byte & (1 << bitPos); - } - - function unpackBinaryFrame(byteArray, frameOffset, pixelsPerFrame) { - // Create a new pixel array given the image size - var pixelData = new Uint8Array(pixelsPerFrame); - - for (var i = 0; i < pixelsPerFrame; i++) { - // Compute byte position - var bytePos = Math.floor(i / 8); - - // Get the current byte - var byte = byteArray[bytePos + frameOffset]; - - // Bit position (0-7) within byte - var bitPos = (i % 8); - - // Check whether bit at bitpos is set - pixelData[i] = isBitSet(byte, bitPos) ? 1 : 0; - } - - return pixelData; - } - - cornerstoneWADOImageLoader.wadouri.unpackBinaryFrame = unpackBinaryFrame; -}($, cornerstone, cornerstoneWADOImageLoader)); -(function ($, cornerstoneWADOImageLoader) { - - "use strict"; - - // the taskId to assign to the next task added via addTask() - var nextTaskId = 0; - - // array of queued tasks sorted with highest priority task first - var tasks = []; - - // array of web workers to dispatch decode tasks to - var webWorkers = []; - - var defaultConfig = { - maxWebWorkers: navigator.hardwareConcurrency || 1, - startWebWorkersOnDemand: true, - webWorkerPath : '../../dist/cornerstoneWADOImageLoaderWebWorker.js', - webWorkerTaskPaths: [], - taskConfiguration: { - 'decodeTask' : { - loadCodecsOnStartup : true, - initializeCodecsOnStartup: false, - codecsPath: '../dist/cornerstoneWADOImageLoaderCodecs.js', - usePDFJS: false - } - } - }; - - var config; - - var statistics = { - maxWebWorkers : 0, - numWebWorkers : 0, - numTasksQueued : 0, - numTasksExecuting : 0, - numTasksCompleted: 0, - totalTaskTimeInMS: 0, - totalTimeDelayedInMS: 0 - }; - - /** - * Function to start a task on a web worker - */ - function startTaskOnWebWorker() { - // return immediately if no decode tasks to do - if(!tasks.length) { - return; - } - - // look for a web worker that is ready - for(var i=0; i < webWorkers.length; i++) { - { - if(webWorkers[i].status === 'ready') { - // mark it as busy so tasks are not assigned to it - webWorkers[i].status = 'busy'; - - // get the highest priority task - var task = tasks.shift(); - task.start = new Date().getTime(); - - // update stats with how long this task was delayed (waiting in queue) - var end = new Date().getTime(); - statistics.totalTimeDelayedInMS += end - task.added; - - // assign this task to this web worker and send the web worker - // a message to execute it - webWorkers[i].task = task; - webWorkers[i].worker.postMessage({ - taskType: task.taskType, - workerIndex: i, - data: task.data - }, task.transferList); - statistics.numTasksExecuting++; - return; - } - } - } - - // if no available web workers and we haven't started max web workers, start a new one - if(webWorkers.length < config.maxWebWorkers) { - spawnWebWorker(); - } - } - - /** - * Function to handle a message from a web worker - * @param msg - */ - function handleMessageFromWorker(msg) { - //console.log('handleMessageFromWorker', msg.data); - if(msg.data.taskType === 'initialize') { - webWorkers[msg.data.workerIndex].status = 'ready'; - startTaskOnWebWorker(); - } else { - statistics.numTasksExecuting--; - webWorkers[msg.data.workerIndex].status = 'ready'; - statistics.numTasksCompleted++; - var end = new Date().getTime(); - statistics.totalTaskTimeInMS += end - webWorkers[msg.data.workerIndex].task.start; - webWorkers[msg.data.workerIndex].task.deferred.resolve(msg.data.result); - webWorkers[msg.data.workerIndex].task = undefined; - startTaskOnWebWorker(); - } - } - - /** - * Spawns a new web worker - */ - function spawnWebWorker() { - // prevent exceeding maxWebWorkers - if(webWorkers.length >= config.maxWebWorkers) { - return; - } - - // spawn the webworker - var worker = new Worker(config.webWorkerPath); - webWorkers.push({ - worker: worker, - status: 'initializing' - }); - worker.addEventListener('message', handleMessageFromWorker); - worker.postMessage({ - taskType: 'initialize', - workerIndex: webWorkers.length - 1, - config: config - }); - } - - /** - * Initialization function for the web worker manager - spawns web workers - * @param configObject - */ - function initialize(configObject) { - configObject = configObject || defaultConfig; - - // prevent being initialized more than once - if(config) { - throw new Error('WebWorkerManager already initialized'); - } - - config = configObject; - - config.maxWebWorkers = config.maxWebWorkers || (navigator.hardwareConcurrency || 1); - - // Spawn new web workers - if(!config.startWebWorkersOnDemand) { - for(var i=0; i < config.maxWebWorkers; i++) { - spawnWebWorker(); - } - } - } - - /** - * dynamically loads a web worker task - * @param sourcePath - * @param taskConfig - */ - function loadWebWorkerTask(sourcePath, taskConfig) { - // add it to the list of web worker tasks paths so on demand web workers - // load this properly - config.webWorkerTaskPaths.push(sourcePath); - - // if a task specific configuration is provided, merge it into the config - if(taskConfig) { - config.taskConfiguration = Object.assign(config.taskConfiguration, taskConfig); - } - - // tell each spawned web worker to load this task - for(var i=0; i < webWorkers.length; i++) { - webWorkers[i].worker.postMessage({ - taskType: 'loadWebWorkerTask', - workerIndex: webWorkers.length - 1, - sourcePath: sourcePath, - config: config - }); - } - } - - /** - * Function to add a decode task to be performed - * - * @param taskType - the taskType for this task - * @param data - data specific to the task - * @param priority - optional priority of the task (defaults to 0), > 0 is higher, < 0 is lower - * @param transferList - optional array of data to transfer to web worker - * @returns {*} - */ - function addTask(taskType, data, priority, transferList) { - if (!config) { - initialize(); - } - - priority = priority || 0; - var deferred = $.Deferred(); - - // find the right spot to insert this decode task (based on priority) - for (var i = 0; i < tasks.length; i++) { - if (tasks[i].priority <= priority) { - break; - } - } - - var taskId = nextTaskId++; - - // insert the decode task at position i - tasks.splice(i, 0, { - taskId: taskId, - taskType: taskType, - status: 'ready', - added: new Date().getTime(), - data: data, - deferred: deferred, - priority: priority, - transferList: transferList - }); - - // try to start a task on the web worker since we just added a new task and a web worker may be available - startTaskOnWebWorker(); - - return { - taskId: taskId, - promise: deferred.promise() - }; - } - - /** - * Changes the priority of a queued task - * @param taskId - the taskId to change the priority of - * @param priority - priority of the task (defaults to 0), > 0 is higher, < 0 is lower - * @returns boolean - true on success, false if taskId not found - */ - function setTaskPriority(taskId, priority) { - // search for this taskId - for (var i = 0; i < tasks.length; i++) { - if (tasks[i].taskId === taskId) { - // taskId found, remove it - var task = tasks.splice(i, 1)[0]; - - // set its prioirty - task.priority = priority; - - // find the right spot to insert this decode task (based on priority) - for (i = 0; i < tasks.length; i++) { - if (tasks[i].priority <= priority) { - break; - } - } - - // insert the decode task at position i - tasks.splice(i, 0, task); - return true; - } - } - return false; - } - - /** - * Cancels a queued task and rejects - * @param taskId - the taskId to cancel - * @param reason - optional reason the task was rejected - * @returns boolean - true on success, false if taskId not found - */ - function cancelTask(taskId, reason) { - // search for this taskId - for (var i = 0; i < tasks.length; i++) { - if (tasks[i].taskId === taskId) { - // taskId found, remove it - var task = tasks.splice(i, 1); - task.promise.reject(reason); - return true; - } - } - return false; - } - - /** - * Function to return the statistics on running web workers - * @returns object containing statistics - */ - function getStatistics() { - statistics.maxWebWorkers = config.maxWebWorkers; - statistics.numWebWorkers = webWorkers.length; - statistics.numTasksQueued = tasks.length; - return statistics; - } - - // module exports - cornerstoneWADOImageLoader.webWorkerManager = { - initialize : initialize, - loadWebWorkerTask: loadWebWorkerTask, - addTask : addTask, - getStatistics: getStatistics, - setTaskPriority: setTaskPriority, - cancelTask: cancelTask - }; - -}($, cornerstoneWADOImageLoader)); -(function ($, cornerstone, cornerstoneWADOImageLoader) { - - "use strict"; - - function xhrRequest(url, imageId, headers) { - headers = headers || {}; - - var deferred = $.Deferred(); - - // Make the request for the DICOM P10 SOP Instance - var xhr = new XMLHttpRequest(); - xhr.open("get", url, true); - xhr.responseType = "arraybuffer"; - cornerstoneWADOImageLoader.internal.options.beforeSend(xhr); - Object.keys(headers).forEach(function (key) { - xhr.setRequestHeader(key, headers[key]); - }); - - // handle response data - xhr.onreadystatechange = function () { - // TODO: consider sending out progress messages here as we receive the pixel data - if (xhr.readyState === 4) { - if (xhr.status === 200) { - deferred.resolve(xhr.response, xhr); - } - else { - // request failed, reject the deferred - deferred.reject(xhr); - } - } - }; - xhr.onprogress = function (oProgress) { - // console.log('progress:',oProgress) - - if (oProgress.lengthComputable) { //evt.loaded the bytes browser receive - //evt.total the total bytes seted by the header - // - var loaded = oProgress.loaded; - var total = oProgress.total; - var percentComplete = Math.round((loaded / total) * 100); - - $(cornerstone).trigger('CornerstoneImageLoadProgress', { - imageId: imageId, - loaded: loaded, - total: total, - percentComplete: percentComplete - }); - } - }; - - xhr.send(); - - return deferred.promise(); - } - - cornerstoneWADOImageLoader.internal.xhrRequest = xhrRequest; -}($, cornerstone, cornerstoneWADOImageLoader)); diff --git a/Packages/ohif-cornerstone/client/dicomParser.js b/Packages/ohif-cornerstone/client/dicomParser.js deleted file mode 100644 index 251463119..000000000 --- a/Packages/ohif-cornerstone/client/dicomParser.js +++ /dev/null @@ -1,2682 +0,0 @@ -/*! dicom-parser - v1.7.4 - 2016-08-18 | (c) 2014 Chris Hafey | https://github.com/chafey/dicomParser */ -(function (root, factory) { - - // node.js - if (typeof module !== 'undefined' && module.exports) { - module.exports = factory(); - } - else if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define([], factory); - } else { - // Browser globals - if(typeof cornerstone === 'undefined'){ - dicomParser = {}; - - // meteor - if (typeof Package !== 'undefined') { - root.dicomParser = dicomParser; - } - } - dicomParser = factory(); - } -}(this, function () { - -/** - * Parses a DICOM P10 byte array and returns a DataSet object with the parsed elements. If the options - * argument is supplied and it contains the untilTag property, parsing will stop once that - * tag is encoutered. This can be used to parse partial byte streams. - * - * @param byteArray the byte array - * @param options object to control parsing behavior (optional) - * @returns {DataSet} - * @throws error if an error occurs while parsing. The exception object will contain a property dataSet with the - * elements successfully parsed before the error. - */ -var dicomParser = (function(dicomParser) { - if(dicomParser === undefined) - { - dicomParser = {}; - } - - dicomParser.parseDicom = function(byteArray, options) { - - if(byteArray === undefined) - { - throw "dicomParser.parseDicom: missing required parameter 'byteArray'"; - } - - function readTransferSyntax(metaHeaderDataSet) { - if(metaHeaderDataSet.elements.x00020010 === undefined) { - throw 'dicomParser.parseDicom: missing required meta header attribute 0002,0010'; - } - var transferSyntaxElement = metaHeaderDataSet.elements.x00020010; - return dicomParser.readFixedString(byteArray, transferSyntaxElement.dataOffset, transferSyntaxElement.length); - } - - function isExplicit(transferSyntax) { - if(transferSyntax === '1.2.840.10008.1.2') // implicit little endian - { - return false; - } - // all other transfer syntaxes should be explicit - return true; - } - - function getDataSetByteStream(transferSyntax, position) { - if(transferSyntax === '1.2.840.10008.1.2.1.99') - { - // if an infalter callback is registered, use it - if (options && options.inflater) { - var fullByteArrayCallback = options.inflater(byteArray, position); - return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, fullByteArrayCallback, 0); - } - // if running on node, use the zlib library to inflate - // http://stackoverflow.com/questions/4224606/how-to-check-whether-a-script-is-running-under-node-js - else if (typeof module !== 'undefined' && this.module !== module) { - // inflate it - var zlib = require('zlib'); - var deflatedBuffer = dicomParser.sharedCopy(byteArray, position, byteArray.length - position); - var inflatedBuffer = zlib.inflateRawSync(deflatedBuffer); - - // create a single byte array with the full header bytes and the inflated bytes - var fullByteArrayBuffer = dicomParser.alloc(byteArray, inflatedBuffer.length + position); - byteArray.copy(fullByteArrayBuffer, 0, 0, position); - inflatedBuffer.copy(fullByteArrayBuffer, position); - return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, fullByteArrayBuffer, 0); - } - // if pako is defined - use it. This is the web browser path - // https://github.com/nodeca/pako - else if(typeof pako !== "undefined") { - // inflate it - var deflated = byteArray.slice(position); - var inflated = pako.inflateRaw(deflated); - - // create a single byte array with the full header bytes and the inflated bytes - var fullByteArray = dicomParser.alloc(byteArray, inflated.length + position); - fullByteArray.set(byteArray.slice(0, position), 0); - fullByteArray.set(inflated, position); - return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, fullByteArray, 0); - } - // throw exception since no inflater is available - else { - throw 'dicomParser.parseDicom: no inflater available to handle deflate transfer syntax'; - } - } - if(transferSyntax === '1.2.840.10008.1.2.2') // explicit big endian - { - return new dicomParser.ByteStream(dicomParser.bigEndianByteArrayParser, byteArray, position); - } - else - { - // all other transfer syntaxes are little endian; only the pixel encoding differs - // make a new stream so the metaheader warnings don't come along for the ride - return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray, position); - } - } - - function mergeDataSets(metaHeaderDataSet, instanceDataSet) - { - for (var propertyName in metaHeaderDataSet.elements) - { - if(metaHeaderDataSet.elements.hasOwnProperty(propertyName)) - { - instanceDataSet.elements[propertyName] = metaHeaderDataSet.elements[propertyName]; - } - } - if (metaHeaderDataSet.warnings !== undefined) { - instanceDataSet.warnings = metaHeaderDataSet.warnings.concat(instanceDataSet.warnings); - } - return instanceDataSet; - } - - function readDataSet(metaHeaderDataSet) - { - var transferSyntax = readTransferSyntax(metaHeaderDataSet); - var explicit = isExplicit(transferSyntax); - var dataSetByteStream = getDataSetByteStream(transferSyntax, metaHeaderDataSet.position); - - var elements = {}; - var dataSet = new dicomParser.DataSet(dataSetByteStream.byteArrayParser, dataSetByteStream.byteArray, elements); - dataSet.warnings = dataSetByteStream.warnings; - - try{ - if(explicit) { - dicomParser.parseDicomDataSetExplicit(dataSet, dataSetByteStream, dataSetByteStream.byteArray.length, options); - } - else - { - dicomParser.parseDicomDataSetImplicit(dataSet, dataSetByteStream, dataSetByteStream.byteArray.length, options); - } - } - catch(e) { - var ex = { - exception: e, - dataSet: dataSet - }; - throw ex; - } - return dataSet; - } - - // main function here - function parseTheByteStream() { - var metaHeaderDataSet = dicomParser.readPart10Header(byteArray, options); - - var dataSet = readDataSet(metaHeaderDataSet); - - return mergeDataSets(metaHeaderDataSet, dataSet); - } - - // This is where we actually start parsing - return parseTheByteStream(); - }; - - return dicomParser; -})(dicomParser); - -/** - * Utility function for creating a basic offset table for JPEG transfer syntaxes - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - // Each JPEG image has an end of image marker 0xFFD9 - function isEndOfImageMarker(dataSet, position) { - return (dataSet.byteArray[position] === 0xFF && - dataSet.byteArray[position + 1] === 0xD9); - } - - function isFragmentEndOfImage(dataSet, pixelDataElement, fragmentIndex) { - var fragment = pixelDataElement.fragments[fragmentIndex]; - // Need to check the last two bytes and the last three bytes for marker since odd length - // fragments are zero padded - if(isEndOfImageMarker(dataSet, fragment.position + fragment.length - 2) || - isEndOfImageMarker(dataSet, fragment.position + fragment.length - 3)) { - return true; - } - return false; - } - - function findLastImageFrameFragmentIndex(dataSet, pixelDataElement, startFragment) { - for(var fragmentIndex=startFragment; fragmentIndex < pixelDataElement.fragments.length; fragmentIndex++) { - if(isFragmentEndOfImage(dataSet, pixelDataElement, fragmentIndex)) { - return fragmentIndex; - } - } - } - - /** - * Creates a basic offset table by scanning fragments for JPEG start of image and end Of Image markers - * @param {object} dataSet - the parsed dicom dataset - * @param {object} pixelDataElement - the pixel data element - * @param [fragments] - optional array of objects describing each fragment (offset, position, length) - * @returns {Array} basic offset table (array of offsets to beginning of each frame) - */ - dicomParser.createJPEGBasicOffsetTable = function(dataSet, pixelDataElement, fragments) { - // Validate parameters - if(dataSet === undefined) { - throw 'dicomParser.createJPEGBasicOffsetTable: missing required parameter dataSet'; - } - if(pixelDataElement === undefined) { - throw 'dicomParser.createJPEGBasicOffsetTable: missing required parameter pixelDataElement'; - } - if(pixelDataElement.tag !== 'x7fe00010') { - throw "dicomParser.createJPEGBasicOffsetTable: parameter 'pixelDataElement' refers to non pixel data tag (expected tag = x7fe00010'"; - } - if(pixelDataElement.encapsulatedPixelData !== true) { - throw "dicomParser.createJPEGBasicOffsetTable: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.hadUndefinedLength !== true) { - throw "dicomParser.createJPEGBasicOffsetTable: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.basicOffsetTable === undefined) { - throw "dicomParser.createJPEGBasicOffsetTable: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.fragments === undefined) { - throw "dicomParser.createJPEGBasicOffsetTable: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.fragments.length <= 0) { - throw "dicomParser.createJPEGBasicOffsetTable: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(fragments && fragments.length <=0) { - throw "dicomParser.createJPEGBasicOffsetTable: parameter 'fragments' must not be zero length"; - } - - // Default values - fragments = fragments || pixelDataElement.fragments; - - var basicOffsetTable = []; - - var startFragmentIndex = 0; - - while(true) { - // Add the offset for the start fragment - basicOffsetTable.push(pixelDataElement.fragments[startFragmentIndex].offset); - var endFragmentIndex = findLastImageFrameFragmentIndex(dataSet, pixelDataElement, startFragmentIndex); - if(endFragmentIndex === undefined || endFragmentIndex === pixelDataElement.fragments.length -1) { - return basicOffsetTable; - } - startFragmentIndex = endFragmentIndex + 1; - } - }; - - return dicomParser; -}(dicomParser)); -var dicomParser = (function (dicomParser) { - "use strict"; - - if (dicomParser === undefined) { - dicomParser = {}; - } - - /** - * converts an explicit dataSet to a javascript object - * @param dataSet - * @param options - */ - dicomParser.explicitDataSetToJS = function (dataSet, options) { - - if(dataSet === undefined) { - throw 'dicomParser.explicitDataSetToJS: missing required parameter dataSet'; - } - - options = options || { - omitPrivateAttibutes: true, // true if private elements should be omitted - maxElementLength : 128 // maximum element length to try and convert to string format - }; - - var result = { - - }; - - for(var tag in dataSet.elements) { - var element = dataSet.elements[tag]; - - // skip this element if it a private element and our options specify that we should - if(options.omitPrivateAttibutes === true && dicomParser.isPrivateTag(tag)) - { - continue; - } - - if(element.items) { - // handle sequences - var sequenceItems = []; - for(var i=0; i < element.items.length; i++) { - sequenceItems.push(dicomParser.explicitDataSetToJS(element.items[i].dataSet, options)); - } - result[tag] = sequenceItems; - } else { - var asString; - asString = undefined; - if(element.length < options.maxElementLength) { - asString = dicomParser.explicitElementToString(dataSet, element); - } - - if(asString !== undefined) { - result[tag] = asString; - } else { - result[tag] = { - dataOffset: element.dataOffset, - length : element.length - }; - } - } - } - - return result; - }; - - - return dicomParser; -}(dicomParser)); -var dicomParser = (function (dicomParser) { - "use strict"; - - if (dicomParser === undefined) { - dicomParser = {}; - } - - /** - * Converts an explicit VR element to a string or undefined if it is not possible to convert. - * Throws an error if an implicit element is supplied - * @param dataSet - * @param element - * @returns {*} - */ - dicomParser.explicitElementToString = function(dataSet, element) - { - if(dataSet === undefined || element === undefined) { - throw 'dicomParser.explicitElementToString: missing required parameters'; - } - if(element.vr === undefined) { - throw 'dicomParser.explicitElementToString: cannot convert implicit element to string'; - } - var vr = element.vr; - var tag = element.tag; - - var textResult; - - function multiElementToString(numItems, func) { - var result = ""; - for(var i=0; i < numItems; i++) { - if(i !== 0) { - result += '/'; - } - result += func.call(dataSet, tag, i).toString(); - } - return result; - } - - if(dicomParser.isStringVr(vr) === true) - { - textResult = dataSet.string(tag); - } - else if (vr == 'AT') { - var num = dataSet.uint32(tag); - if(num === undefined) { - return undefined; - } - if (num < 0) - { - num = 0xFFFFFFFF + num + 1; - } - - return 'x' + num.toString(16).toUpperCase(); - } - else if (vr == 'US') - { - textResult = multiElementToString(element.length / 2, dataSet.uint16); - } - else if(vr === 'SS') - { - textResult = multiElementToString(element.length / 2, dataSet.int16); - } - else if (vr == 'UL') - { - textResult = multiElementToString(element.length / 4, dataSet.uint32); - } - else if(vr === 'SL') - { - textResult = multiElementToString(element.length / 4, dataSet.int32); - } - else if(vr == 'FD') - { - textResult = multiElementToString(element.length / 8, dataSet.double); - } - else if(vr == 'FL') - { - textResult = multiElementToString(element.length / 4, dataSet.float); - } - - return textResult; - }; - return dicomParser; -}(dicomParser)); -/** - * Utility functions for dealing with DICOM - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - // algorithm based on http://stackoverflow.com/questions/1433030/validate-number-of-days-in-a-given-month - function daysInMonth(m, y) { // m is 0 indexed: 0-11 - switch (m) { - case 2 : - return (y % 4 == 0 && y % 100) || y % 400 == 0 ? 29 : 28; - case 9 : case 4 : case 6 : case 11 : - return 30; - default : - return 31 - } - } - - function isValidDate(d, m, y) { - // make year is a number - if(isNaN(y)) { - return false; - } - return m > 0 && m <= 12 && d > 0 && d <= daysInMonth(m, y); - } - - - /** - * Parses a DA formatted string into a Javascript object - * @param {string} date a string in the DA VR format - * @param {boolean} [validate] - true if an exception should be thrown if the date is invalid - * @returns {*} Javascript object with properties year, month and day or undefined if not present or not 8 bytes long - */ - dicomParser.parseDA = function(date, validate) - { - if(date && date.length === 8) - { - var yyyy = parseInt(date.substring(0, 4), 10); - var mm = parseInt(date.substring(4, 6), 10); - var dd = parseInt(date.substring(6, 8), 10); - - if(validate) { - if (isValidDate(dd, mm, yyyy) !== true) { - throw "invalid DA '" + date + "'"; - } - } - return { - year: yyyy, - month: mm, - day: dd - }; - } - if(validate) { - throw "invalid DA '" + date + "'"; - } - return undefined; - }; - - return dicomParser; -}(dicomParser)); -/** - * Utility functions for dealing with DICOM - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * Parses a TM formatted string into a javascript object with properties for hours, minutes, seconds and fractionalSeconds - * @param {string} time - a string in the TM VR format - * @param {boolean} [validate] - true if an exception should be thrown if the date is invalid - * @returns {*} javascript object with properties for hours, minutes, seconds and fractionalSeconds or undefined if no element or data. Missing fields are set to undefined - */ - dicomParser.parseTM = function(time, validate) { - - if (time.length >= 2) // must at least have HH - { - // 0123456789 - // HHMMSS.FFFFFF - var hh = parseInt(time.substring(0, 2), 10); - var mm = time.length >= 4 ? parseInt(time.substring(2, 4), 10) : undefined; - var ss = time.length >= 6 ? parseInt(time.substring(4, 6), 10) : undefined; - var ffffff = time.length >= 8 ? parseInt(time.substring(7, 13), 10) : undefined; - - if(validate) { - if((isNaN(hh)) || - (mm !== undefined && isNaN(mm)) || - (ss !== undefined && isNaN(ss)) || - (ffffff !== undefined && isNaN(ffffff)) || - (hh < 0 || hh > 23) || - (mm && (mm <0 || mm > 59)) || - (ss && (ss <0 || ss > 59)) || - (ffffff && (ffffff <0 || ffffff > 999999))) - { - throw "invalid TM '" + time + "'"; - } - } - - return { - hours: hh, - minutes: mm, - seconds: ss, - fractionalSeconds: ffffff - }; - } - - if(validate) { - throw "invalid TM '" + time + "'"; - } - - return undefined; - }; - - return dicomParser; -}(dicomParser)); -/** - * Utility functions for dealing with DICOM - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - var stringVrs = { - AE: true, - AS: true, - AT: false, - CS: true, - DA: true, - DS: true, - DT: true, - FL: false, - FD: false, - IS: true, - LO: true, - LT: true, - OB: false, - OD: false, - OF: false, - OW: false, - PN: true, - SH: true, - SL: false, - SQ: false, - SS: false, - ST: true, - TM: true, - UI: true, - UL: false, - UN: undefined, // dunno - UR: true, - US: false, - UT: true - }; - - /** - * Tests to see if vr is a string or not. - * @param vr - * @returns true if string, false it not string, undefined if unknown vr or UN type - */ - dicomParser.isStringVr = function(vr) - { - return stringVrs[vr]; - }; - - /** - * Tests to see if a given tag in the format xggggeeee is a private tag or not - * @param tag - * @returns {boolean} - */ - dicomParser.isPrivateTag = function(tag) - { - var lastGroupDigit = parseInt(tag[4]); - var groupIsOdd = (lastGroupDigit % 2) === 1; - return groupIsOdd; - }; - - /** - * Parses a PN formatted string into a javascript object with properties for givenName, familyName, middleName, prefix and suffix - * @param personName a string in the PN VR format - * @param index - * @returns {*} javascript object with properties for givenName, familyName, middleName, prefix and suffix or undefined if no element or data - */ - dicomParser.parsePN = function(personName) { - if(personName === undefined) { - return undefined; - } - var stringValues = personName.split('^'); - return { - familyName: stringValues[0], - givenName: stringValues[1], - middleName: stringValues[2], - prefix: stringValues[3], - suffix: stringValues[4] - }; - }; - - - - return dicomParser; -}(dicomParser)); -/** - * Functionality for extracting encapsulated pixel data - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - var deprecatedNoticeLogged = false; - - /** - * Returns the pixel data for the specified frame in an encapsulated pixel data element. If no basic offset - * table is present, it assumes that all fragments are for one frame. Note that this assumption/logic is not - * valid for multi-frame instances so this function has been deprecated and will eventually be removed. Code - * should be updated to use readEncapsulatedPixelDataFromFragments() or readEncapsulatedImageFrame() - * - * @deprecated since version 1.6 - use readEncapsulatedPixelDataFromFragments() or readEncapsulatedImageFrame() - * @param dataSet - the dataSet containing the encapsulated pixel data - * @param pixelDataElement - the pixel data element (x7fe00010) to extract the frame from - * @param frame - the zero based frame index - * @returns {object} with the encapsulated pixel data - */ - - - dicomParser.readEncapsulatedPixelData = function(dataSet, pixelDataElement, frame) - { - if(!deprecatedNoticeLogged) { - deprecatedNoticeLogged = true; - if(console && console.log) { - console.log("WARNING: dicomParser.readEncapsulatedPixelData() has been deprecated"); - } - } - - if(dataSet === undefined) { - throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'dataSet'"; - } - if(pixelDataElement === undefined) { - throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'element'"; - } - if(frame === undefined) { - throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'frame'"; - } - if(pixelDataElement.tag !== 'x7fe00010') { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to non pixel data tag (expected tag = x7fe00010'"; - } - if(pixelDataElement.encapsulatedPixelData !== true) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.hadUndefinedLength !== true) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.basicOffsetTable === undefined) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.fragments === undefined) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; - } - if(frame < 0) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'frame' must be >= 0"; - } - - // If the basic offset table is not empty, we can extract the frame - if(pixelDataElement.basicOffsetTable.length !== 0) - { - return dicomParser.readEncapsulatedImageFrame(dataSet, pixelDataElement, frame); - } - else - { - // No basic offset table, assume all fragments are for one frame - NOTE that this is NOT a valid - // assumption but is the original behavior so we are keeping it for now - return dicomParser.readEncapsulatedPixelDataFromFragments(dataSet, pixelDataElement, 0, pixelDataElement.fragments.length); - } - }; - - return dicomParser; -}(dicomParser)); - -/** - * - * Internal helper function to allocate new byteArray buffers - */ -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * Creates a new byteArray of the same type (Uint8Array or Buffer) of the specified length. - * @param byteArray the underlying byteArray (either Uint8Array or Buffer) - * @param length number of bytes of the Byte Array - * @returns {object} Uint8Array or Buffer depending on the type of byteArray - */ - dicomParser.alloc = function(byteArray, length) { - if (typeof Buffer !== 'undefined' && byteArray instanceof Buffer) { - return Buffer.alloc(length); - } - else if(byteArray instanceof Uint8Array) { - return new Uint8Array(length); - } else { - throw 'dicomParser.alloc: unknown type for byteArray'; - } - }; - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing different types from a big-endian byte array - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - dicomParser.bigEndianByteArrayParser = { - /** - * - * Parses an unsigned int 16 from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 16 - * @throws error if buffer overread would occur - * @access private - */ - readUint16: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readUint16: position cannot be less than 0'; - } - if (position + 2 > byteArray.length) { - throw 'bigEndianByteArrayParser.readUint16: attempt to read past end of buffer'; - } - return (byteArray[position] << 8) + byteArray[position + 1]; - }, - - /** - * - * Parses a signed int 16 from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed signed int 16 - * @throws error if buffer overread would occur - * @access private - */ - readInt16: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readInt16: position cannot be less than 0'; - } - if (position + 2 > byteArray.length) { - throw 'bigEndianByteArrayParser.readInt16: attempt to read past end of buffer'; - } - var int16 = (byteArray[position] << 8) + byteArray[position + 1]; - // fix sign - if (int16 & 0x8000) { - int16 = int16 - 0xFFFF - 1; - } - return int16; - }, - - /** - * Parses an unsigned int 32 from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 32 - * @throws error if buffer overread would occur - * @access private - */ - readUint32: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readUint32: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'bigEndianByteArrayParser.readUint32: attempt to read past end of buffer'; - } - - var uint32 = (256 * (256 * (256 * byteArray[position] + - byteArray[position + 1]) + - byteArray[position + 2]) + - byteArray[position + 3]); - - return uint32; - }, - - /** - * Parses a signed int 32 from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed signed int 32 - * @throws error if buffer overread would occur - * @access private - */ - readInt32: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readInt32: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'bigEndianByteArrayParser.readInt32: attempt to read past end of buffer'; - } - - var int32 = ((byteArray[position] << 24) + - (byteArray[position + 1] << 16) + - (byteArray[position + 2] << 8) + - byteArray[position + 3]); - - return int32; - }, - - /** - * Parses 32-bit float from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed 32-bit float - * @throws error if buffer overread would occur - * @access private - */ - readFloat: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readFloat: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'bigEndianByteArrayParser.readFloat: attempt to read past end of buffer'; - } - - // I am sure there is a better way than this but this should be safe - var byteArrayForParsingFloat = new Uint8Array(4); - byteArrayForParsingFloat[3] = byteArray[position]; - byteArrayForParsingFloat[2] = byteArray[position + 1]; - byteArrayForParsingFloat[1] = byteArray[position + 2]; - byteArrayForParsingFloat[0] = byteArray[position + 3]; - var floatArray = new Float32Array(byteArrayForParsingFloat.buffer); - return floatArray[0]; - }, - - /** - * Parses 64-bit float from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed 64-bit float - * @throws error if buffer overread would occur - * @access private - */ - readDouble: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readDouble: position cannot be less than 0'; - } - - if (position + 8 > byteArray.length) { - throw 'bigEndianByteArrayParser.readDouble: attempt to read past end of buffer'; - } - - // I am sure there is a better way than this but this should be safe - var byteArrayForParsingFloat = new Uint8Array(8); - byteArrayForParsingFloat[7] = byteArray[position]; - byteArrayForParsingFloat[6] = byteArray[position + 1]; - byteArrayForParsingFloat[5] = byteArray[position + 2]; - byteArrayForParsingFloat[4] = byteArray[position + 3]; - byteArrayForParsingFloat[3] = byteArray[position + 4]; - byteArrayForParsingFloat[2] = byteArray[position + 5]; - byteArrayForParsingFloat[1] = byteArray[position + 6]; - byteArrayForParsingFloat[0] = byteArray[position + 7]; - var floatArray = new Float64Array(byteArrayForParsingFloat.buffer); - return floatArray[0]; - } - }; - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions common to parsing byte arrays of any type - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * Reads a string of 8-bit characters from an array of bytes and advances - * the position by length bytes. A null terminator will end the string - * but will not effect advancement of the position. Trailing and leading - * spaces are preserved (not trimmed) - * @param byteArray the byteArray to read from - * @param position the position in the byte array to read from - * @param length the maximum number of bytes to parse - * @returns {string} the parsed string - * @throws error if buffer overread would occur - * @access private - */ - dicomParser.readFixedString = function(byteArray, position, length) - { - if(length < 0) - { - throw 'dicomParser.readFixedString - length cannot be less than 0'; - } - - if(position + length > byteArray.length) { - throw 'dicomParser.readFixedString: attempt to read past end of buffer'; - } - - var result = ""; - var byte; - for(var i=0; i < length; i++) - { - byte = byteArray[position + i]; - if(byte === 0) { - position += length; - return result; - } - result += String.fromCharCode(byte); - } - - return result; - }; - - - return dicomParser; -}(dicomParser)); -/** - * - * Internal helper class to assist with parsing. Supports reading from a byte - * stream contained in a Uint8Array. Example usage: - * - * var byteArray = new Uint8Array(32); - * var byteStream = new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray); - * - * */ -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * Constructor for ByteStream objects. - * @param byteArrayParser a parser for parsing the byte array - * @param byteArray a Uint8Array containing the byte stream - * @param position (optional) the position to start reading from. 0 if not specified - * @constructor - * @throws will throw an error if the byteArrayParser parameter is not present - * @throws will throw an error if the byteArray parameter is not present or invalid - * @throws will throw an error if the position parameter is not inside the byte array - */ - dicomParser.ByteStream = function(byteArrayParser, byteArray, position) { - if(byteArrayParser === undefined) - { - throw "dicomParser.ByteStream: missing required parameter 'byteArrayParser'"; - } - if(byteArray === undefined) - { - throw "dicomParser.ByteStream: missing required parameter 'byteArray'"; - } - if((byteArray instanceof Uint8Array) === false && - (byteArray instanceof Buffer) === false ) { - throw 'dicomParser.ByteStream: parameter byteArray is not of type Uint8Array or Buffer'; - } - if(position < 0) - { - throw "dicomParser.ByteStream: parameter 'position' cannot be less than 0"; - } - if(position >= byteArray.length) - { - throw "dicomParser.ByteStream: parameter 'position' cannot be greater than or equal to 'byteArray' length"; - - } - this.byteArrayParser = byteArrayParser; - this.byteArray = byteArray; - this.position = position ? position : 0; - this.warnings = []; // array of string warnings encountered while parsing - }; - - /** - * Safely seeks through the byte stream. Will throw an exception if an attempt - * is made to seek outside of the byte array. - * @param offset the number of bytes to add to the position - * @throws error if seek would cause position to be outside of the byteArray - */ - dicomParser.ByteStream.prototype.seek = function(offset) - { - if(this.position + offset < 0) - { - throw "dicomParser.ByteStream.prototype.seek: cannot seek to position < 0"; - } - this.position += offset; - }; - - /** - * Returns a new ByteStream object from the current position and of the requested number of bytes - * @param numBytes the length of the byte array for the ByteStream to contain - * @returns {dicomParser.ByteStream} - * @throws error if buffer overread would occur - */ - dicomParser.ByteStream.prototype.readByteStream = function(numBytes) - { - if(this.position + numBytes > this.byteArray.length) { - throw 'dicomParser.ByteStream.prototype.readByteStream: readByteStream - buffer overread'; - } - var byteArrayView = dicomParser.sharedCopy(this.byteArray, this.position, numBytes); - this.position += numBytes; - return new dicomParser.ByteStream(this.byteArrayParser, byteArrayView); - }; - - /** - * - * Parses an unsigned int 16 from a byte array and advances - * the position by 2 bytes - * - * @returns {*} the parsed unsigned int 16 - * @throws error if buffer overread would occur - */ - dicomParser.ByteStream.prototype.readUint16 = function() - { - var result = this.byteArrayParser.readUint16(this.byteArray, this.position); - this.position += 2; - return result; - }; - - /** - * Parses an unsigned int 32 from a byte array and advances - * the position by 2 bytes - * - * @returns {*} the parse unsigned int 32 - * @throws error if buffer overread would occur - */ - dicomParser.ByteStream.prototype.readUint32 = function() - { - var result = this.byteArrayParser.readUint32(this.byteArray, this.position); - this.position += 4; - return result; - }; - - /** - * Reads a string of 8-bit characters from an array of bytes and advances - * the position by length bytes. A null terminator will end the string - * but will not effect advancement of the position. - * @param length the maximum number of bytes to parse - * @returns {string} the parsed string - * @throws error if buffer overread would occur - */ - dicomParser.ByteStream.prototype.readFixedString = function(length) - { - var result = dicomParser.readFixedString(this.byteArray, this.position, length); - this.position += length; - return result; - }; - - return dicomParser; -}(dicomParser)); -/** - * - * The DataSet class encapsulates a collection of DICOM Elements and provides various functions - * to access the data in those elements - * - * Rules for handling padded spaces: - * DS = Strip leading and trailing spaces - * DT = Strip trailing spaces - * IS = Strip leading and trailing spaces - * PN = Strip trailing spaces - * TM = Strip trailing spaces - * AE = Strip leading and trailing spaces - * CS = Strip leading and trailing spaces - * SH = Strip leading and trailing spaces - * LO = Strip leading and trailing spaces - * LT = Strip trailing spaces - * ST = Strip trailing spaces - * UT = Strip trailing spaces - * - */ -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - function getByteArrayParser(element, defaultParser) - { - return (element.parser !== undefined ? element.parser : defaultParser); - } - - /** - * Constructs a new DataSet given byteArray and collection of elements - * @param byteArrayParser - * @param byteArray - * @param elements - * @constructor - */ - dicomParser.DataSet = function(byteArrayParser, byteArray, elements) - { - this.byteArrayParser = byteArrayParser; - this.byteArray = byteArray; - this.elements = elements; - }; - - /** - * Finds the element for tag and returns an unsigned int 16 if it exists and has data - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the value in a multivalued element. Default is index 0 if not supplied - * @returns {*} unsigned int 16 or undefined if the attribute is not present or has data of length 0 - */ - dicomParser.DataSet.prototype.uint16 = function(tag, index) - { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readUint16(this.byteArray, element.dataOffset + (index *2)); - } - return undefined; - }; - - /** - * Finds the element for tag and returns an signed int 16 if it exists and has data - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the value in a multivalued element. Default is index 0 if not supplied - * @returns {*} signed int 16 or undefined if the attribute is not present or has data of length 0 - */ - dicomParser.DataSet.prototype.int16 = function(tag, index) - { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readInt16(this.byteArray, element.dataOffset + (index * 2)); - } - return undefined; - }; - - /** - * Finds the element for tag and returns an unsigned int 32 if it exists and has data - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the value in a multivalued element. Default is index 0 if not supplied - * @returns {*} unsigned int 32 or undefined if the attribute is not present or has data of length 0 - */ - dicomParser.DataSet.prototype.uint32 = function(tag, index) - { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readUint32(this.byteArray, element.dataOffset + (index * 4)); - } - return undefined; - }; - - /** - * Finds the element for tag and returns an signed int 32 if it exists and has data - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the value in a multivalued element. Default is index 0 if not supplied - * @returns {*} signed int 32 or undefined if the attribute is not present or has data of length 0 - */ - dicomParser.DataSet.prototype.int32 = function(tag, index) - { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readInt32(this.byteArray, element.dataOffset + (index * 4)); - } - return undefined; - }; - - /** - * Finds the element for tag and returns a 32 bit floating point number (VR=FL) if it exists and has data - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the value in a multivalued element. Default is index 0 if not supplied - * @returns {*} float or undefined if the attribute is not present or has data of length 0 - */ - dicomParser.DataSet.prototype.float = function(tag, index) - { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readFloat(this.byteArray, element.dataOffset + (index * 4)); - } - return undefined; - }; - - /** - * Finds the element for tag and returns a 64 bit floating point number (VR=FD) if it exists and has data - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the value in a multivalued element. Default is index 0 if not supplied - * @returns {*} float or undefined if the attribute is not present or doesn't has data of length 0 - */ - dicomParser.DataSet.prototype.double = function(tag, index) - { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readDouble(this.byteArray, element.dataOffset + (index * 8)); - } - return undefined; - }; - - /** - * Returns the number of string values for the element - * @param tag The DICOM tag in the format xGGGGEEEE - * @returns {*} the number of string values or undefined if the attribute is not present or has zero length data - */ - dicomParser.DataSet.prototype.numStringValues = function(tag) - { - var element = this.elements[tag]; - if(element && element.length > 0) - { - var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); - var numMatching = fixedString.match(/\\/g); - if(numMatching === null) - { - return 1; - } - return numMatching.length + 1; - } - return undefined; - }; - - /** - * Returns a string for the element. If index is provided, the element is assumed to be - * multi-valued and will return the component specified by index. Undefined is returned - * if there is no component with the specified index, the element does not exist or is zero length. - * - * Use this function for VR types of AE, CS, SH and LO - * - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the desired value in a multi valued string or undefined for the entire string - * @returns {*} - */ - dicomParser.DataSet.prototype.string = function(tag, index) - { - var element = this.elements[tag]; - if(element && element.length > 0) - { - var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); - if(index >= 0) - { - var values = fixedString.split('\\'); - // trim trailing spaces - return values[index].trim(); - } - else - { - // trim trailing spaces - return fixedString.trim(); - } - } - return undefined; - }; - - /** - * Returns a string with the leading spaces preserved and trailing spaces removed. - * - * Use this function to access data for VRs of type UT, ST and LT - * - * @param tag - * @param index - * @returns {*} - */ - dicomParser.DataSet.prototype.text = function(tag, index) - { - var element = this.elements[tag]; - if(element && element.length > 0) - { - var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); - if(index >= 0) - { - var values = fixedString.split('\\'); - return values[index].replace(/ +$/, ''); - } - else - { - return fixedString.replace(/ +$/, ''); - } - } - return undefined; - }; - - /** - * Parses a string to a float for the specified index in a multi-valued element. If index is not specified, - * the first value in a multi-valued VR will be parsed if present. - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the desired value in a multi valued string or undefined for the first value - * @returns {*} a floating point number or undefined if not present or data not long enough - */ - dicomParser.DataSet.prototype.floatString = function(tag, index) - { - var element = this.elements[tag]; - if(element && element.length > 0) - { - index = (index !== undefined) ? index : 0; - var value = this.string(tag, index); - if(value !== undefined) { - return parseFloat(value); - } - } - return undefined; - }; - - /** - * Parses a string to an integer for the specified index in a multi-valued element. If index is not specified, - * the first value in a multi-valued VR will be parsed if present. - * @param tag The DICOM tag in the format xGGGGEEEE - * @param index the index of the desired value in a multi valued string or undefined for the first value - * @returns {*} an integer or undefined if not present or data not long enough - */ - dicomParser.DataSet.prototype.intString = function(tag, index) - { - var element = this.elements[tag]; - if(element && element.length > 0) { - index = (index !== undefined) ? index : 0; - var value = this.string(tag, index); - if(value !== undefined) { - return parseInt(value); - } - } - return undefined; - }; - - //dicomParser.DataSet = DataSet; - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * reads from the byte stream until it finds the magic number for the Sequence Delimitation Item item - * and then sets the length of the element - * @param byteStream - * @param element - */ - dicomParser.findAndSetUNElementLength = function(byteStream, element) - { - if(byteStream === undefined) - { - throw "dicomParser.findAndSetUNElementLength: missing required parameter 'byteStream'"; - } - - var itemDelimitationItemLength = 8; // group, element, length - var maxPosition = byteStream.byteArray.length - itemDelimitationItemLength; - while(byteStream.position <= maxPosition) - { - var groupNumber; - groupNumber = byteStream.readUint16(); - if(groupNumber === 0xfffe) - { - var elementNumber; - elementNumber = byteStream.readUint16(); - if(elementNumber === 0xe0dd) - { - // NOTE: It would be better to also check for the length to be 0 as part of the check above - // but we will just log a warning for now - var itemDelimiterLength; - itemDelimiterLength = byteStream.readUint32(); // the length - if(itemDelimiterLength !== 0) { - byteStream.warnings('encountered non zero length following item delimiter at position' + byteStream.position - 4 + " while reading element of undefined length with tag ' + element.tag"); - } - element.length = byteStream.position - element.dataOffset; - return; - } - } - } - - // No item delimitation item - silently set the length to the end of the buffer and set the position past the end of the buffer - element.length = byteStream.byteArray.length - element.dataOffset; - byteStream.seek(byteStream.byteArray.length - byteStream.position); - }; - - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * Reads an encapsulated pixel data element and adds an array of fragments to the element - * containing the offset and length of each fragment and any offsets from the basic offset - * table - * @param byteStream - * @param element - */ - dicomParser.findEndOfEncapsulatedElement = function(byteStream, element, warnings) - { - if(byteStream === undefined) - { - throw "dicomParser.findEndOfEncapsulatedElement: missing required parameter 'byteStream'"; - } - if(element === undefined) - { - throw "dicomParser.findEndOfEncapsulatedElement: missing required parameter 'element'"; - } - - element.encapsulatedPixelData = true; - element.basicOffsetTable = []; - element.fragments = []; - var basicOffsetTableItemTag = dicomParser.readTag(byteStream); - if(basicOffsetTableItemTag !== 'xfffee000') { - throw "dicomParser.findEndOfEncapsulatedElement: basic offset table not found"; - } - var basicOffsetTableItemlength = byteStream.readUint32(); - var numFragments = basicOffsetTableItemlength / 4; - for(var i =0; i < numFragments; i++) { - var offset = byteStream.readUint32(); - element.basicOffsetTable.push(offset); - } - var baseOffset = byteStream.position; - - while(byteStream.position < byteStream.byteArray.length) - { - var tag = dicomParser.readTag(byteStream); - var length = byteStream.readUint32(); - if(tag === 'xfffee0dd') - { - byteStream.seek(length); - element.length = byteStream.position - element.dataOffset; - return; - } - else if(tag === 'xfffee000') - { - element.fragments.push({ - offset: byteStream.position - baseOffset - 8, - position : byteStream.position, - length : length - }); - } - else { - if(warnings) { - warnings.push('unexpected tag ' + tag + ' while searching for end of pixel data element with undefined length'); - } - if(length > byteStream.byteArray.length - byteStream.position) - { - // fix length - length = byteStream.byteArray.length - byteStream.position; - } - element.fragments.push({ - offset: byteStream.position - baseOffset - 8, - position : byteStream.position, - length : length - }); - byteStream.seek(length); - element.length = byteStream.position - element.dataOffset; - return; - } - - byteStream.seek(length); - } - - if(warnings) { - warnings.push("pixel data element " + element.tag + " missing sequence delimiter tag xfffee0dd"); - } - }; - - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * reads from the byte stream until it finds the magic numbers for the item delimitation item - * and then sets the length of the element - * @param byteStream - * @param element - */ - dicomParser.findItemDelimitationItemAndSetElementLength = function(byteStream, element) - { - if(byteStream === undefined) - { - throw "dicomParser.readDicomElementImplicit: missing required parameter 'byteStream'"; - } - - var itemDelimitationItemLength = 8; // group, element, length - var maxPosition = byteStream.byteArray.length - itemDelimitationItemLength; - while(byteStream.position <= maxPosition) - { - var groupNumber = byteStream.readUint16(); - if(groupNumber === 0xfffe) - { - var elementNumber = byteStream.readUint16(); - if(elementNumber === 0xe00d) - { - // NOTE: It would be better to also check for the length to be 0 as part of the check above - // but we will just log a warning for now - var itemDelimiterLength = byteStream.readUint32(); // the length - if(itemDelimiterLength !== 0) { - byteStream.warnings('encountered non zero length following item delimiter at position' + byteStream.position - 4 + " while reading element of undefined length with tag ' + element.tag"); - } - element.length = byteStream.position - element.dataOffset; - return; - } - } - } - - // No item delimitation item - silently set the length to the end of the buffer and set the position past the end of the buffer - element.length = byteStream.byteArray.length - element.dataOffset; - byteStream.seek(byteStream.byteArray.length - byteStream.position); - }; - - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing different types from a little-endian byte array - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - dicomParser.littleEndianByteArrayParser = { - /** - * - * Parses an unsigned int 16 from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 16 - * @throws error if buffer overread would occur - * @access private - */ - readUint16: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readUint16: position cannot be less than 0'; - } - if (position + 2 > byteArray.length) { - throw 'littleEndianByteArrayParser.readUint16: attempt to read past end of buffer'; - } - return byteArray[position] + (byteArray[position + 1] * 256); - }, - - /** - * - * Parses a signed int 16 from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed signed int 16 - * @throws error if buffer overread would occur - * @access private - */ - readInt16: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readInt16: position cannot be less than 0'; - } - if (position + 2 > byteArray.length) { - throw 'littleEndianByteArrayParser.readInt16: attempt to read past end of buffer'; - } - var int16 = byteArray[position] + (byteArray[position + 1] << 8); - // fix sign - if (int16 & 0x8000) { - int16 = int16 - 0xFFFF - 1; - } - return int16; - }, - - - /** - * Parses an unsigned int 32 from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 32 - * @throws error if buffer overread would occur - * @access private - */ - readUint32: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readUint32: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'littleEndianByteArrayParser.readUint32: attempt to read past end of buffer'; - } - - var uint32 = (byteArray[position] + - (byteArray[position + 1] * 256) + - (byteArray[position + 2] * 256 * 256) + - (byteArray[position + 3] * 256 * 256 * 256 )); - - return uint32; - }, - - /** - * Parses a signed int 32 from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 32 - * @throws error if buffer overread would occur - * @access private - */ - readInt32: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readInt32: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'littleEndianByteArrayParser.readInt32: attempt to read past end of buffer'; - } - - var int32 = (byteArray[position] + - (byteArray[position + 1] << 8) + - (byteArray[position + 2] << 16) + - (byteArray[position + 3] << 24)); - - return int32; - - }, - - /** - * Parses 32-bit float from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed 32-bit float - * @throws error if buffer overread would occur - * @access private - */ - readFloat: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readFloat: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'littleEndianByteArrayParser.readFloat: attempt to read past end of buffer'; - } - - // I am sure there is a better way than this but this should be safe - var byteArrayForParsingFloat = new Uint8Array(4); - byteArrayForParsingFloat[0] = byteArray[position]; - byteArrayForParsingFloat[1] = byteArray[position + 1]; - byteArrayForParsingFloat[2] = byteArray[position + 2]; - byteArrayForParsingFloat[3] = byteArray[position + 3]; - var floatArray = new Float32Array(byteArrayForParsingFloat.buffer); - return floatArray[0]; - }, - - /** - * Parses 64-bit float from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed 64-bit float - * @throws error if buffer overread would occur - * @access private - */ - readDouble: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readDouble: position cannot be less than 0'; - } - - if (position + 8 > byteArray.length) { - throw 'littleEndianByteArrayParser.readDouble: attempt to read past end of buffer'; - } - - // I am sure there is a better way than this but this should be safe - var byteArrayForParsingFloat = new Uint8Array(8); - byteArrayForParsingFloat[0] = byteArray[position]; - byteArrayForParsingFloat[1] = byteArray[position + 1]; - byteArrayForParsingFloat[2] = byteArray[position + 2]; - byteArrayForParsingFloat[3] = byteArray[position + 3]; - byteArrayForParsingFloat[4] = byteArray[position + 4]; - byteArrayForParsingFloat[5] = byteArray[position + 5]; - byteArrayForParsingFloat[6] = byteArray[position + 6]; - byteArrayForParsingFloat[7] = byteArray[position + 7]; - var floatArray = new Float64Array(byteArrayForParsingFloat.buffer); - return floatArray[0]; - } - }; - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing implicit and explicit DICOM data sets - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * reads an explicit data set - * @param byteStream the byte stream to read from - * @param maxPosition the maximum position to read up to (optional - only needed when reading sequence items) - */ - dicomParser.parseDicomDataSetExplicit = function (dataSet, byteStream, maxPosition, options) { - - maxPosition = (maxPosition === undefined) ? byteStream.byteArray.length : maxPosition ; - options = options || {}; - - if(byteStream === undefined) - { - throw "dicomParser.parseDicomDataSetExplicit: missing required parameter 'byteStream'"; - } - if(maxPosition < byteStream.position || maxPosition > byteStream.byteArray.length) - { - throw "dicomParser.parseDicomDataSetExplicit: invalid value for parameter 'maxPosition'"; - } - var elements = dataSet.elements; - - while(byteStream.position < maxPosition) - { - var element = dicomParser.readDicomElementExplicit(byteStream, dataSet.warnings, options.untilTag); - elements[element.tag] = element; - if(element.tag === options.untilTag) { - return; - } - } - if(byteStream.position > maxPosition) { - throw "dicomParser:parseDicomDataSetExplicit: buffer overrun"; - } - }; - - /** - * reads an implicit data set - * @param byteStream the byte stream to read from - * @param maxPosition the maximum position to read up to (optional - only needed when reading sequence items) - */ - dicomParser.parseDicomDataSetImplicit = function(dataSet, byteStream, maxPosition, options) - { - maxPosition = (maxPosition === undefined) ? dataSet.byteArray.length : maxPosition ; - options = options || {}; - - if(byteStream === undefined) - { - throw "dicomParser.parseDicomDataSetImplicit: missing required parameter 'byteStream'"; - } - if(maxPosition < byteStream.position || maxPosition > byteStream.byteArray.length) - { - throw "dicomParser.parseDicomDataSetImplicit: invalid value for parameter 'maxPosition'"; - } - - var elements = dataSet.elements; - - while(byteStream.position < maxPosition) - { - var element = dicomParser.readDicomElementImplicit(byteStream, options.untilTag, options.vrCallback); - elements[element.tag] = element; - if(element.tag === options.untilTag) { - return; - } - } - }; - - return dicomParser; -}(dicomParser)); - -/** - * Internal helper functions for for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - function getDataLengthSizeInBytesForVR(vr) - { - if( vr === 'OB' || - vr === 'OW' || - vr === 'SQ' || - vr === 'OF' || - vr === 'UT' || - vr === 'UN') - { - return 4; - } - else - { - return 2; - } - } - - dicomParser.readDicomElementExplicit = function(byteStream, warnings, untilTag) - { - if(byteStream === undefined) - { - throw "dicomParser.readDicomElementExplicit: missing required parameter 'byteStream'"; - } - - var element = { - tag : dicomParser.readTag(byteStream), - vr : byteStream.readFixedString(2) - // length set below based on VR - // dataOffset set below based on VR and size of length - }; - - var dataLengthSizeBytes = getDataLengthSizeInBytesForVR(element.vr); - if(dataLengthSizeBytes === 2) - { - element.length = byteStream.readUint16(); - element.dataOffset = byteStream.position; - } - else - { - byteStream.seek(2); - element.length = byteStream.readUint32(); - element.dataOffset = byteStream.position; - } - - if(element.length === 4294967295) - { - element.hadUndefinedLength = true; - } - - if(element.tag === untilTag) { - return element; - } - - // if VR is SQ, parse the sequence items - if(element.vr === 'SQ') - { - dicomParser.readSequenceItemsExplicit(byteStream, element, warnings); - return element; - } - - - if(element.length === 4294967295) - { - if(element.tag === 'x7fe00010') { - dicomParser.findEndOfEncapsulatedElement(byteStream, element, warnings); - return element; - } else if(element.vr === 'UN') { - dicomParser.findAndSetUNElementLength(byteStream, element); - return element; - } else { - dicomParser.findItemDelimitationItemAndSetElementLength(byteStream, element); - return element; - } - } - - byteStream.seek(element.length); - return element; - }; - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - function isSequence(element, byteStream, vrCallback) { - // if a data dictionary callback was provided, use that to verify that the element is a sequence. - if (typeof vrCallback !== 'undefined') { - return (vrCallback(element.tag) === 'SQ'); - } - if ((byteStream.position + 4) <= byteStream.byteArray.length) { - var nextTag = dicomParser.readTag(byteStream); - byteStream.seek(-4); - // Item start tag (fffe,e000) or sequence delimiter (i.e. end of sequence) tag (0fffe,e0dd) - // These are the tags that could potentially be found directly after a sequence start tag (the delimiter - // is found in the case of an empty sequence). This is not 100% safe because a non-sequence item - // could have data that has these bytes, but this is how to do it without a data dictionary. - return (nextTag === 'xfffee000') || (nextTag === 'xfffee0dd'); - } - byteStream.warnings.push('eof encountered before finding sequence item tag or sequence delimiter tag in peeking to determine VR'); - return false; - } - - dicomParser.readDicomElementImplicit = function(byteStream, untilTag, vrCallback) - { - if(byteStream === undefined) - { - throw "dicomParser.readDicomElementImplicit: missing required parameter 'byteStream'"; - } - - var element = { - tag : dicomParser.readTag(byteStream), - length: byteStream.readUint32(), - dataOffset : byteStream.position - }; - - if(element.length === 4294967295) { - element.hadUndefinedLength = true; - } - - if(element.tag === untilTag) { - return element; - } - - if (isSequence(element, byteStream, vrCallback)) { - // parse the sequence - dicomParser.readSequenceItemsImplicit(byteStream, element); - return element; - } - - // if element is not a sequence and has undefined length, we have to - // scan the data for a magic number to figure out when it ends. - if(element.hadUndefinedLength) - { - dicomParser.findItemDelimitationItemAndSetElementLength(byteStream, element); - return element; - } - - // non sequence element with known length, skip over the data part - byteStream.seek(element.length); - return element; - }; - - - return dicomParser; -}(dicomParser)); -/** - * Functionality for extracting encapsulated pixel data - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - function findFragmentIndexWithOffset(fragments, offset) { - for(var i=0; i < fragments.length; i++) { - if(fragments[i].offset === offset) { - return i; - } - } - } - - function calculateNumberOfFragmentsForFrame(frameIndex, basicOffsetTable, fragments, startFragmentIndex) { - // special case for last frame - if(frameIndex === basicOffsetTable.length -1) { - return fragments.length - startFragmentIndex; - } - - // iterate through each fragment looking for the one matching the offset for the next frame - var nextFrameOffset = basicOffsetTable[frameIndex + 1]; - for(var i=startFragmentIndex + 1; i < fragments.length; i++) { - if(fragments[i].offset === nextFrameOffset) { - return i - startFragmentIndex; - } - } - - throw "dicomParser.calculateNumberOfFragmentsForFrame: could not find fragment with offset matching basic offset table"; - } - - /** - * Returns the pixel data for the specified frame in an encapsulated pixel data element that has a non - * empty basic offset table. Note that this function will fail if the basic offset table is empty - in that - * case you need to determine which fragments map to which frames and read them using - * readEncapsulatedPixelDataFromFragments(). Also see the function createJEPGBasicOffsetTable() to see - * how a basic offset table can be created for JPEG images - * - * @param dataSet - the dataSet containing the encapsulated pixel data - * @param pixelDataElement - the pixel data element (x7fe00010) to extract the frame from - * @param frameIndex - the zero based frame index - * @param [basicOffsetTable] - optional array of starting offsets for frames - * @param [fragments] - optional array of objects describing each fragment (offset, position, length) - * @returns {object} with the encapsulated pixel data - */ - dicomParser.readEncapsulatedImageFrame = function(dataSet, pixelDataElement, frameIndex, basicOffsetTable, fragments) - { - // default parameters - basicOffsetTable = basicOffsetTable || pixelDataElement.basicOffsetTable; - fragments = fragments || pixelDataElement.fragments; - - // Validate parameters - if(dataSet === undefined) { - throw "dicomParser.readEncapsulatedImageFrame: missing required parameter 'dataSet'"; - } - if(pixelDataElement === undefined) { - throw "dicomParser.readEncapsulatedImageFrame: missing required parameter 'pixelDataElement'"; - } - if(frameIndex === undefined) { - throw "dicomParser.readEncapsulatedImageFrame: missing required parameter 'frameIndex'"; - } - if(basicOffsetTable === undefined) { - throw "dicomParser.readEncapsulatedImageFrame: parameter 'pixelDataElement' does not have basicOffsetTable"; - } - if(pixelDataElement.tag !== 'x7fe00010') { - throw "dicomParser.readEncapsulatedImageFrame: parameter 'pixelDataElement' refers to non pixel data tag (expected tag = x7fe00010'"; - } - if(pixelDataElement.encapsulatedPixelData !== true) { - throw "dicomParser.readEncapsulatedImageFrame: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.hadUndefinedLength !== true) { - throw "dicomParser.readEncapsulatedImageFrame: parameter 'pixelDataElement' refers to pixel data element that does not have undefined length"; - } - if(pixelDataElement.fragments === undefined) { - throw "dicomParser.readEncapsulatedImageFrame: parameter 'pixelDataElement' refers to pixel data element that does not have fragments"; - } - if(basicOffsetTable.length === 0) { - throw "dicomParser.readEncapsulatedImageFrame: basicOffsetTable has zero entries"; - } - if(frameIndex < 0) { - throw "dicomParser.readEncapsulatedImageFrame: parameter 'frameIndex' must be >= 0"; - } - if(frameIndex >= basicOffsetTable.length) { - throw "dicomParser.readEncapsulatedImageFrame: parameter 'frameIndex' must be < basicOffsetTable.length"; - } - - // find starting fragment based on the offset for the frame in the basic offset table - var offset = basicOffsetTable[frameIndex]; - var startFragmentIndex = findFragmentIndexWithOffset(fragments, offset); - if(startFragmentIndex === undefined) { - throw "dicomParser.readEncapsulatedImageFrame: unable to find fragment that matches basic offset table entry"; - } - - // calculate the number of fragments for this frame - var numFragments = calculateNumberOfFragmentsForFrame(frameIndex, basicOffsetTable, fragments, startFragmentIndex); - - // now extract the frame from the fragments - return dicomParser.readEncapsulatedPixelDataFromFragments(dataSet, pixelDataElement, startFragmentIndex, numFragments, fragments); - }; - - return dicomParser; -}(dicomParser)); - -/** - * Functionality for extracting encapsulated pixel data - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - function calculateBufferSize(fragments, startFragment, numFragments) { - var bufferSize = 0; - for(var i=startFragment; i < startFragment + numFragments; i++) { - bufferSize += fragments[i].length; - } - return bufferSize; - } - - /** - * Returns the encapsulated pixel data from the specified fragments. Use this function when you know - * the fragments you want to extract data from. See - * - * @param dataSet - the dataSet containing the encapsulated pixel data - * @param pixelDataElement - the pixel data element (x7fe00010) to extract the fragment data from - * @param startFragmentIndex - zero based index of the first fragment to extract from - * @param [numFragments] - the number of fragments to extract from, default is 1 - * @param [fragments] - optional array of objects describing each fragment (offset, position, length) - * @returns {object} byte array with the encapsulated pixel data - */ - dicomParser.readEncapsulatedPixelDataFromFragments = function(dataSet, pixelDataElement, startFragmentIndex, numFragments, fragments) - { - // default values - numFragments = numFragments || 1; - fragments = fragments || pixelDataElement.fragments; - - // check parameters - if(dataSet === undefined) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: missing required parameter 'dataSet'"; - } - if(pixelDataElement === undefined) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: missing required parameter 'pixelDataElement'"; - } - if(startFragmentIndex === undefined) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: missing required parameter 'startFragmentIndex'"; - } - if(numFragments === undefined) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: missing required parameter 'numFragments'"; - } - if(pixelDataElement.tag !== 'x7fe00010') { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'pixelDataElement' refers to non pixel data tag (expected tag = x7fe00010'"; - } - if(pixelDataElement.encapsulatedPixelData !== true) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.hadUndefinedLength !== true) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.basicOffsetTable === undefined) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.fragments === undefined) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.fragments.length <= 0) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'pixelDataElement' refers to pixel data element that does not have encapsulated pixel data"; - } - if(startFragmentIndex < 0) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'startFragmentIndex' must be >= 0"; - } - if(startFragmentIndex >= pixelDataElement.fragments.length) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'startFragmentIndex' must be < number of fragments"; - } - if(numFragments < 1) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'numFragments' must be > 0"; - } - if(startFragmentIndex + numFragments > pixelDataElement.fragments.length) { - throw "dicomParser.readEncapsulatedPixelDataFromFragments: parameter 'startFragment' + 'numFragments' < number of fragments"; - } - - // create byte stream on the data for this pixel data element - var byteStream = new dicomParser.ByteStream(dataSet.byteArrayParser, dataSet.byteArray, pixelDataElement.dataOffset); - - // seek past the basic offset table (no need to parse it again since we already have) - var basicOffsetTable = dicomParser.readSequenceItem(byteStream); - if(basicOffsetTable.tag !== 'xfffee000') - { - throw "dicomParser.readEncapsulatedPixelData: missing basic offset table xfffee000"; - } - byteStream.seek(basicOffsetTable.length); - - var fragmentZeroPosition = byteStream.position; - var fragmentHeaderSize = 8; // tag + length - - // if there is only one fragment, return a view on this array to avoid copying - if(numFragments === 1) { - return dicomParser.sharedCopy(byteStream.byteArray, fragmentZeroPosition + fragments[startFragmentIndex].offset + fragmentHeaderSize, fragments[startFragmentIndex].length); - } - - // more than one fragment, combine all of the fragments into one buffer - var bufferSize = calculateBufferSize(fragments, startFragmentIndex, numFragments); - - var pixelData = dicomParser.alloc(byteStream.byteArray, bufferSize); - - var pixelDataIndex = 0; - for(var i=startFragmentIndex; i < startFragmentIndex + numFragments; i++) { - var fragmentOffset = fragmentZeroPosition + fragments[i].offset + fragmentHeaderSize; - for(var j=0; j < fragments[i].length; j++) { - pixelData[pixelDataIndex++] = byteStream.byteArray[fragmentOffset++]; - } - } - - return pixelData; - }; - - return dicomParser; -}(dicomParser)); - -/** - * Parses a DICOM P10 byte array and returns a DataSet object with the parsed elements. If the options - * argument is supplied and it contains the untilTag property, parsing will stop once that - * tag is encoutered. This can be used to parse partial byte streams. - * - * @param byteArray the byte array - * @param options object to control parsing behavior (optional) - * @returns {DataSet} - * @throws error if an error occurs while parsing. The exception object will contain a property dataSet with the - * elements successfully parsed before the error. - */ -var dicomParser = (function(dicomParser) { - if(dicomParser === undefined) - { - dicomParser = {}; - } - - dicomParser.readPart10Header = function(byteArray, options) { - - if(byteArray === undefined) - { - throw "dicomParser.readPart10Header: missing required parameter 'byteArray'"; - } - - var littleEndianByteStream = new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray); - - function readPrefix() - { - littleEndianByteStream.seek(128); - var prefix = littleEndianByteStream.readFixedString(4); - if(prefix !== "DICM") - { - throw "dicomParser.readPart10Header: DICM prefix not found at location 132 - this is not a valid DICOM P10 file."; - } - } - - // main function here - function readTheHeader() { - // Per the DICOM standard, the header is always encoded in Explicit VR Little Endian (see PS3.10, section 7.1) - // so use littleEndianByteStream throughout this method regardless of the transfer syntax - readPrefix(); - - var warnings = []; - var elements = {}; - while(littleEndianByteStream.position < littleEndianByteStream.byteArray.length) { - var position = littleEndianByteStream.position; - var element = dicomParser.readDicomElementExplicit(littleEndianByteStream, warnings); - if(element.tag > 'x0002ffff') { - littleEndianByteStream.position = position; - break; - } - // Cache the littleEndianByteArrayParser for meta header elements, since the rest of the data set may be big endian - // and this parser will be needed later if the meta header values are to be read. - element.parser = dicomParser.littleEndianByteArrayParser; - elements[element.tag] = element; - } - var metaHeaderDataSet = new dicomParser.DataSet(littleEndianByteStream.byteArrayParser, littleEndianByteStream.byteArray, elements); - metaHeaderDataSet.warnings = littleEndianByteStream.warnings; - metaHeaderDataSet.position = littleEndianByteStream.position; - return metaHeaderDataSet; - } - - // This is where we actually start parsing - return readTheHeader(); - }; - - return dicomParser; -})(dicomParser); - -/** - * Internal helper functions for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - function readDicomDataSetExplicitUndefinedLength(byteStream, warnings) - { - var elements = {}; - - while(byteStream.position < byteStream.byteArray.length) - { - var element = dicomParser.readDicomElementExplicit(byteStream, warnings); - elements[element.tag] = element; - - // we hit an item delimiter tag, return the current offset to mark - // the end of this sequence item - if(element.tag === 'xfffee00d') - { - return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); - } - - } - - // eof encountered - log a warning and return what we have for the element - warnings.push('eof encountered before finding item delimiter tag while reading sequence item of undefined length'); - return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); - } - - function readSequenceItemExplicit(byteStream, warnings) - { - var item = dicomParser.readSequenceItem(byteStream); - - if(item.length === 4294967295) - { - item.hadUndefinedLength = true; - item.dataSet = readDicomDataSetExplicitUndefinedLength(byteStream, warnings); - item.length = byteStream.position - item.dataOffset; - } - else - { - item.dataSet = new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, {}); - dicomParser.parseDicomDataSetExplicit(item.dataSet, byteStream, byteStream.position + item.length); - } - return item; - } - - function readSQElementUndefinedLengthExplicit(byteStream, element, warnings) - { - while((byteStream.position + 4) <= byteStream.byteArray.length) - { - // end reading this sequence if the next tag is the sequence delimitation item - var nextTag = dicomParser.readTag(byteStream); - byteStream.seek(-4); - if (nextTag === 'xfffee0dd') { - // set the correct length - element.length = byteStream.position - element.dataOffset; - byteStream.seek(8); - return element; - } - - var item = readSequenceItemExplicit(byteStream, warnings); - element.items.push(item); - } - warnings.push('eof encountered before finding sequence delimitation tag while reading sequence of undefined length'); - element.length = byteStream.position - element.dataOffset; - } - - function readSQElementKnownLengthExplicit(byteStream, element, warnings) - { - var maxPosition = element.dataOffset + element.length; - while(byteStream.position < maxPosition) - { - var item = readSequenceItemExplicit(byteStream, warnings); - element.items.push(item); - } - } - - dicomParser.readSequenceItemsExplicit = function(byteStream, element, warnings) - { - if(byteStream === undefined) - { - throw "dicomParser.readSequenceItemsExplicit: missing required parameter 'byteStream'"; - } - if(element === undefined) - { - throw "dicomParser.readSequenceItemsExplicit: missing required parameter 'element'"; - } - - element.items = []; - - if(element.length === 4294967295) - { - readSQElementUndefinedLengthExplicit(byteStream, element, warnings); - } - else - { - readSQElementKnownLengthExplicit(byteStream, element, warnings); - } - }; - - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - function readDicomDataSetImplicitUndefinedLength(byteStream, vrCallback) - { - var elements = {}; - - while(byteStream.position < byteStream.byteArray.length) - { - var element = dicomParser.readDicomElementImplicit(byteStream, undefined, vrCallback); - elements[element.tag] = element; - - // we hit an item delimiter tag, return the current offset to mark - // the end of this sequence item - if(element.tag === 'xfffee00d') - { - return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); - } - } - // eof encountered - log a warning and return what we have for the element - byteStream.warnings.push('eof encountered before finding sequence item delimiter in sequence item of undefined length'); - return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); - } - - function readSequenceItemImplicit(byteStream, vrCallback) - { - var item = dicomParser.readSequenceItem(byteStream); - - if(item.length === 4294967295) - { - item.hadUndefinedLength = true; - item.dataSet = readDicomDataSetImplicitUndefinedLength(byteStream, vrCallback); - item.length = byteStream.position - item.dataOffset; - } - else - { - item.dataSet = new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, {}); - dicomParser.parseDicomDataSetImplicit(item.dataSet, byteStream, byteStream.position + item.length, {vrCallback: vrCallback}); - } - return item; - } - - function readSQElementUndefinedLengthImplicit(byteStream, element, vrCallback) - { - while((byteStream.position + 4) <= byteStream.byteArray.length) - { - // end reading this sequence if the next tag is the sequence delimitation item - var nextTag = dicomParser.readTag(byteStream); - byteStream.seek(-4); - if (nextTag === 'xfffee0dd') { - // set the correct length - element.length = byteStream.position - element.dataOffset; - byteStream.seek(8); - return element; - } - - var item = readSequenceItemImplicit(byteStream, vrCallback); - element.items.push(item); - } - byteStream.warnings.push('eof encountered before finding sequence delimiter in sequence of undefined length'); - element.length = byteStream.byteArray.length - element.dataOffset; - } - - function readSQElementKnownLengthImplicit(byteStream, element, vrCallback) - { - var maxPosition = element.dataOffset + element.length; - while(byteStream.position < maxPosition) - { - var item = readSequenceItemImplicit(byteStream, vrCallback); - element.items.push(item); - } - } - - /** - * Reads sequence items for an element in an implicit little endian byte stream - * @param byteStream the implicit little endian byte stream - * @param element the element to read the sequence items for - * @param vrCallback an optional method that returns a VR string given a tag - */ - dicomParser.readSequenceItemsImplicit = function(byteStream, element, vrCallback) - { - if(byteStream === undefined) - { - throw "dicomParser.readSequenceItemsImplicit: missing required parameter 'byteStream'"; - } - if(element === undefined) - { - throw "dicomParser.readSequenceItemsImplicit: missing required parameter 'element'"; - } - - element.items = []; - - if(element.length === 4294967295) - { - readSQElementUndefinedLengthImplicit(byteStream, element, vrCallback); - } - else - { - readSQElementKnownLengthImplicit(byteStream, element, vrCallback); - } - }; - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * Reads the tag and length of a sequence item and returns them as an object with the following properties - * tag : string for the tag of this element in the format xggggeeee - * length: the number of bytes in this item or 4294967295 if undefined - * dataOffset: the offset into the byteStream of the data for this item - * @param byteStream the byte - * @returns {{tag: string, length: integer, dataOffset: integer}} - */ - dicomParser.readSequenceItem = function(byteStream) - { - if(byteStream === undefined) - { - throw "dicomParser.readSequenceItem: missing required parameter 'byteStream'"; - } - - var element = { - tag : dicomParser.readTag(byteStream), - length : byteStream.readUint32(), - dataOffset : byteStream.position - }; - - if (element.tag !== 'xfffee000') { - var startPosition = byteStream.position; - throw "dicomParser.readSequenceItem: item tag (FFFE,E000) not found at offset " + startPosition; - } - - return element; - }; - - - return dicomParser; -}(dicomParser)); -/** - * Internal helper functions for parsing DICOM elements - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * Reads a tag (group number and element number) from a byteStream - * @param byteStream the byte stream to read from - * @returns {string} the tag in format xggggeeee where gggg is the lowercase hex value of the group number - * and eeee is the lower case hex value of the element number - */ - dicomParser.readTag = function(byteStream) - { - if(byteStream === undefined) - { - throw "dicomParser.readTag: missing required parameter 'byteStream'"; - } - - var groupNumber = byteStream.readUint16() * 256 * 256; - var elementNumber = byteStream.readUint16(); - var tag = "x" + ('00000000' + (groupNumber + elementNumber).toString(16)).substr(-8); - return tag; - }; - - return dicomParser; -}(dicomParser)); -/** - * - * Internal helper function to create a shared copy of a byteArray - * - */ -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - /** - * Creates a view of the underlying byteArray. The view is of the same type as the byteArray (e.g. - * Uint8Array or Buffer) and shares the same underlying memory (changing one changes the other) - * @param byteArray the underlying byteArray (either Uint8Array or Buffer) - * @param byteOffset offset into the underlying byteArray to create the view of - * @param length number of bytes in the view - * @returns {object} Uint8Array or Buffer depending on the type of byteArray - */ - dicomParser.sharedCopy = function(byteArray, byteOffset, length) { - if (typeof Buffer !== 'undefined' && byteArray instanceof Buffer) { - return byteArray.slice(byteOffset, byteOffset + length); - } - else if(byteArray instanceof Uint8Array) { - return new Uint8Array(byteArray.buffer, byteArray.byteOffset + byteOffset, length); - } else { - throw 'dicomParser.from: unknown type for byteArray'; - } - }; - - return dicomParser; -}(dicomParser)); -/** - * Version - */ - -var dicomParser = (function (dicomParser) -{ - "use strict"; - - if(dicomParser === undefined) - { - dicomParser = {}; - } - - dicomParser.version = "1.7.3"; - - return dicomParser; -}(dicomParser)); - return dicomParser; -})); diff --git a/Packages/ohif-cornerstone/client/hammer.js b/Packages/ohif-cornerstone/client/hammer.js deleted file mode 100644 index 9cd1ba7a5..000000000 --- a/Packages/ohif-cornerstone/client/hammer.js +++ /dev/null @@ -1,3240 +0,0 @@ -/*! Hammer.JS - v2.0.8 - 2016-09-30 - * http://hammerjs.github.io/ - * - * Copyright (c) Jorik Tangelder; - * Licensed under the MIT license */ -(function(window, document, exportName, undefined) { -'use strict'; -/** - * @private - * use the val2 when val1 is undefined - * @param {*} val1 - * @param {*} val2 - * @returns {*} - */ -function ifUndefined(val1, val2) { - return val1 === undefined ? val2 : val1; -} - -var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; -var TEST_ELEMENT = document.createElement('div'); - -var TYPE_FUNCTION = 'function'; - -var round = Math.round; -var abs = Math.abs; -var now = Date.now; - -/** - * @private - * get the prefixed property - * @param {Object} obj - * @param {String} property - * @returns {String|Undefined} prefixed - */ -function prefixed(obj, property) { - var prefix = void 0; - var prop = void 0; - var camelProp = property[0].toUpperCase() + property.slice(1); - - var i = 0; - while (i < VENDOR_PREFIXES.length) { - prefix = VENDOR_PREFIXES[i]; - prop = prefix ? prefix + camelProp : property; - - if (prop in obj) { - return prop; - } - i++; - } - return undefined; -} - -function getTouchActionProps() { - if (!NATIVE_TOUCH_ACTION) { - return false; - } - var touchMap = {}; - var cssSupports = window.CSS && window.CSS.supports; - ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function (val) { - - // If css.supports is not supported but there is native touch-action assume it supports - // all values. This is the case for IE 10 and 11. - return touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; - }); - return touchMap; -} - -var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); -var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; - -// magical touchAction value -var TOUCH_ACTION_COMPUTE = 'compute'; -var TOUCH_ACTION_AUTO = 'auto'; -var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented -var TOUCH_ACTION_NONE = 'none'; -var TOUCH_ACTION_PAN_X = 'pan-x'; -var TOUCH_ACTION_PAN_Y = 'pan-y'; -var TOUCH_ACTION_MAP = getTouchActionProps(); - -var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; - -var SUPPORT_TOUCH = 'ontouchstart' in window; -var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; -var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); - -var INPUT_TYPE_TOUCH = 'touch'; -var INPUT_TYPE_PEN = 'pen'; -var INPUT_TYPE_MOUSE = 'mouse'; -var INPUT_TYPE_KINECT = 'kinect'; - -var COMPUTE_INTERVAL = 25; - -var INPUT_START = 1; -var INPUT_MOVE = 2; -var INPUT_END = 4; -var INPUT_CANCEL = 8; - -var DIRECTION_NONE = 1; -var DIRECTION_LEFT = 2; -var DIRECTION_RIGHT = 4; -var DIRECTION_UP = 8; -var DIRECTION_DOWN = 16; - -var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; -var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; -var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; - -var PROPS_XY = ['x', 'y']; -var PROPS_CLIENT_XY = ['clientX', 'clientY']; - -var STATE_POSSIBLE = 1; -var STATE_BEGAN = 2; -var STATE_CHANGED = 4; -var STATE_ENDED = 8; -var STATE_RECOGNIZED = STATE_ENDED; -var STATE_CANCELLED = 16; -var STATE_FAILED = 32; - -/** - * @private - * extend object. - * means that properties in dest will be overwritten by the ones in src. - * @param {Object} target - * @param {...Object} objects_to_assign - * @returns {Object} target - */ -var assign = void 0; -if (typeof Object.assign !== 'function') { - assign = function assign(target) { - if (target === undefined || target === null) { - throw new TypeError('Cannot convert undefined or null to object'); - } - - var output = Object(target); - for (var index = 1; index < arguments.length; index++) { - var source = arguments[index]; - if (source !== undefined && source !== null) { - for (var nextKey in source) { - if (source.hasOwnProperty(nextKey)) { - output[nextKey] = source[nextKey]; - } - } - } - } - return output; - }; -} else { - assign = Object.assign; -} - -var assign$1 = assign; - -/** - * @private - * get a unique id - * @returns {number} uniqueId - */ -var _uniqueId = 1; -function uniqueId() { - return _uniqueId++; -} - -/** - * @private - * walk objects and arrays - * @param {Object} obj - * @param {Function} iterator - * @param {Object} context - */ -function each(obj, iterator, context) { - var i = void 0; - - if (!obj) { - return; - } - - if (obj.forEach) { - obj.forEach(iterator, context); - } else if (obj.length !== undefined) { - i = 0; - while (i < obj.length) { - iterator.call(context, obj[i], i, obj); - i++; - } - } else { - for (i in obj) { - obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); - } - } -} - -/** - * @private - * if the argument is an array, we want to execute the fn on each entry - * if it aint an array we don't want to do a thing. - * this is used by all the methods that accept a single and array argument. - * @param {*|Array} arg - * @param {String} fn - * @param {Object} [context] - * @returns {Boolean} - */ -function invokeArrayArg(arg, fn, context) { - if (Array.isArray(arg)) { - each(arg, context[fn], context); - return true; - } - return false; -} - -/** - * @private - * find if a array contains the object using indexOf or a simple polyFill - * @param {Array} src - * @param {String} find - * @param {String} [findByKey] - * @return {Boolean|Number} false when not found, or the index - */ -function inArray(src, find, findByKey) { - if (src.indexOf && !findByKey) { - return src.indexOf(find); - } else { - var i = 0; - while (i < src.length) { - if (findByKey && src[i][findByKey] == find || !findByKey && src[i] === find) { - // do not use === here, test fails - return i; - } - i++; - } - return -1; - } -} - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; -} : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; -}; - -var asyncGenerator = function () { - function AwaitValue(value) { - this.value = value; - } - - function AsyncGenerator(gen) { - var front, back; - - function send(key, arg) { - return new Promise(function (resolve, reject) { - var request = { - key: key, - arg: arg, - resolve: resolve, - reject: reject, - next: null - }; - - if (back) { - back = back.next = request; - } else { - front = back = request; - resume(key, arg); - } - }); - } - - function resume(key, arg) { - try { - var result = gen[key](arg); - var value = result.value; - - if (value instanceof AwaitValue) { - Promise.resolve(value.value).then(function (arg) { - resume("next", arg); - }, function (arg) { - resume("throw", arg); - }); - } else { - settle(result.done ? "return" : "normal", result.value); - } - } catch (err) { - settle("throw", err); - } - } - - function settle(type, value) { - switch (type) { - case "return": - front.resolve({ - value: value, - done: true - }); - break; - - case "throw": - front.reject(value); - break; - - default: - front.resolve({ - value: value, - done: false - }); - break; - } - - front = front.next; - - if (front) { - resume(front.key, front.arg); - } else { - back = null; - } - } - - this._invoke = send; - - if (typeof gen.return !== "function") { - this.return = undefined; - } - } - - if (typeof Symbol === "function" && Symbol.asyncIterator) { - AsyncGenerator.prototype[Symbol.asyncIterator] = function () { - return this; - }; - } - - AsyncGenerator.prototype.next = function (arg) { - return this._invoke("next", arg); - }; - - AsyncGenerator.prototype.throw = function (arg) { - return this._invoke("throw", arg); - }; - - AsyncGenerator.prototype.return = function (arg) { - return this._invoke("return", arg); - }; - - return { - wrap: function (fn) { - return function () { - return new AsyncGenerator(fn.apply(this, arguments)); - }; - }, - await: function (value) { - return new AwaitValue(value); - } - }; -}(); - -var classCallCheck = function (instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } -}; - -var createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; -}(); - -var get = function get(object, property, receiver) { - if (object === null) object = Function.prototype; - var desc = Object.getOwnPropertyDescriptor(object, property); - - if (desc === undefined) { - var parent = Object.getPrototypeOf(object); - - if (parent === null) { - return undefined; - } else { - return get(parent, property, receiver); - } - } else if ("value" in desc) { - return desc.value; - } else { - var getter = desc.get; - - if (getter === undefined) { - return undefined; - } - - return getter.call(receiver); - } -}; - -var inherits = function (subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); - } - - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - enumerable: false, - writable: true, - configurable: true - } - }); - if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; -}; - -var possibleConstructorReturn = function (self, call) { - if (!self) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - - return call && (typeof call === "object" || typeof call === "function") ? call : self; -}; - -var slicedToArray = function () { - function sliceIterator(arr, i) { - var _arr = []; - var _n = true; - var _d = false; - var _e = undefined; - - try { - for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { - _arr.push(_s.value); - - if (i && _arr.length === i) break; - } - } catch (err) { - _d = true; - _e = err; - } finally { - try { - if (!_n && _i["return"]) _i["return"](); - } finally { - if (_d) throw _e; - } - } - - return _arr; - } - - return function (arr, i) { - if (Array.isArray(arr)) { - return arr; - } else if (Symbol.iterator in Object(arr)) { - return sliceIterator(arr, i); - } else { - throw new TypeError("Invalid attempt to destructure non-iterable instance"); - } - }; -}(); - -/** - * @private - * let a boolean value also be a function that must return a boolean - * this first item in args will be used as the context - * @param {Boolean|Function} val - * @param {Array} [args] - * @returns {Boolean} - */ -function boolOrFn(val, args) { - if ((typeof val === 'undefined' ? 'undefined' : _typeof(val)) === TYPE_FUNCTION) { - return val.apply(args ? args[0] || undefined : undefined, args); - } - return val; -} - -/** - * @private - * get a recognizer by name if it is bound to a manager - * @param {Recognizer|String} otherRecognizer - * @param {Recognizer} recognizer - * @returns {Recognizer} - */ -function getRecognizerByNameIfManager(otherRecognizer, recognizer) { - var manager = recognizer.manager; - - if (manager) { - return manager.get(otherRecognizer); - } - return otherRecognizer; -} - -/** - * @private - * get a usable string, used as event postfix - * @param {constant} state - * @returns {String} state - */ -function stateStr(state) { - if (state & STATE_CANCELLED) { - return 'cancel'; - } else if (state & STATE_ENDED) { - return 'end'; - } else if (state & STATE_CHANGED) { - return 'move'; - } else if (state & STATE_BEGAN) { - return 'start'; - } - return ''; -} - -/** - * @private - * Recognizer flow explained; * - * All recognizers have the initial state of POSSIBLE when a input session starts. - * The definition of a input session is from the first input until the last input, with all it's movement in it. * - * Example session for mouse-input: mousedown -> mousemove -> mouseup - * - * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed - * which determines with state it should be. - * - * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to - * POSSIBLE to give it another change on the next cycle. - * - * Possible - * | - * +-----+---------------+ - * | | - * +-----+-----+ | - * | | | - * Failed Cancelled | - * +-------+------+ - * | | - * Recognized Began - * | - * Changed - * | - * Ended/Recognized - */ - -/** - * @private - * Recognizer - * Every recognizer needs to extend from this class. - * @constructor - * @param {Object} options - */ - -var Recognizer = function () { - function Recognizer(options) { - classCallCheck(this, Recognizer); - - this.options = assign$1({}, this.defaults, options || {}); - - this.id = uniqueId(); - - this.manager = null; - - // default is enable true - this.options.enable = ifUndefined(this.options.enable, true); - - this.state = STATE_POSSIBLE; - this.simultaneous = {}; - this.requireFail = []; - } - - /** - * @private - * set options - * @param {Object} options - * @return {Recognizer} - */ - - - createClass(Recognizer, [{ - key: 'set', - value: function set(options) { - assign$1(this.options, options); - - // also update the touchAction, in case something changed about the directions/enabled state - this.manager && this.manager.touchAction.update(); - return this; - } - - /** - * @private - * recognize simultaneous with an other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - - }, { - key: 'recognizeWith', - value: function recognizeWith(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { - return this; - } - - var simultaneous = this.simultaneous; - - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - if (!simultaneous[otherRecognizer.id]) { - simultaneous[otherRecognizer.id] = otherRecognizer; - otherRecognizer.recognizeWith(this); - } - return this; - } - - /** - * @private - * drop the simultaneous link. it doesnt remove the link on the other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - - }, { - key: 'dropRecognizeWith', - value: function dropRecognizeWith(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { - return this; - } - - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - delete this.simultaneous[otherRecognizer.id]; - return this; - } - - /** - * @private - * recognizer can only run when an other is failing - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - - }, { - key: 'requireFailure', - value: function requireFailure(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { - return this; - } - - var requireFail = this.requireFail; - - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - if (inArray(requireFail, otherRecognizer) === -1) { - requireFail.push(otherRecognizer); - otherRecognizer.requireFailure(this); - } - return this; - } - - /** - * @private - * drop the requireFailure link. it does not remove the link on the other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - - }, { - key: 'dropRequireFailure', - value: function dropRequireFailure(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { - return this; - } - - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - var index = inArray(this.requireFail, otherRecognizer); - if (index > -1) { - this.requireFail.splice(index, 1); - } - return this; - } - - /** - * @private - * has require failures boolean - * @returns {boolean} - */ - - }, { - key: 'hasRequireFailures', - value: function hasRequireFailures() { - return this.requireFail.length > 0; - } - - /** - * @private - * if the recognizer can recognize simultaneous with an other recognizer - * @param {Recognizer} otherRecognizer - * @returns {Boolean} - */ - - }, { - key: 'canRecognizeWith', - value: function canRecognizeWith(otherRecognizer) { - return !!this.simultaneous[otherRecognizer.id]; - } - - /** - * @private - * You should use `tryEmit` instead of `emit` directly to check - * that all the needed recognizers has failed before emitting. - * @param {Object} input - */ - - }, { - key: 'emit', - value: function emit(input) { - var self = this; - var state = this.state; - - - function emit(event) { - self.manager.emit(event, input); - } - - // 'panstart' and 'panmove' - if (state < STATE_ENDED) { - emit(self.options.event + stateStr(state)); - } - - emit(self.options.event); // simple 'eventName' events - - if (input.additionalEvent) { - // additional event(panleft, panright, pinchin, pinchout...) - emit(input.additionalEvent); - } - - // panend and pancancel - if (state >= STATE_ENDED) { - emit(self.options.event + stateStr(state)); - } - } - - /** - * @private - * Check that all the require failure recognizers has failed, - * if true, it emits a gesture event, - * otherwise, setup the state to FAILED. - * @param {Object} input - */ - - }, { - key: 'tryEmit', - value: function tryEmit(input) { - if (this.canEmit()) { - return this.emit(input); - } - // it's failing anyway - this.state = STATE_FAILED; - } - - /** - * @private - * can we emit? - * @returns {boolean} - */ - - }, { - key: 'canEmit', - value: function canEmit() { - var i = 0; - while (i < this.requireFail.length) { - if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { - return false; - } - i++; - } - return true; - } - - /** - * @private - * update the recognizer - * @param {Object} inputData - */ - - }, { - key: 'recognize', - value: function recognize(inputData) { - // make a new copy of the inputData - // so we can change the inputData without messing up the other recognizers - var inputDataClone = assign$1({}, inputData); - - // is is enabled and allow recognizing? - if (!boolOrFn(this.options.enable, [this, inputDataClone])) { - this.reset(); - this.state = STATE_FAILED; - return; - } - - // reset when we've reached the end - if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { - this.state = STATE_POSSIBLE; - } - - this.state = this.process(inputDataClone); - - // the recognizer has recognized a gesture - // so trigger an event - if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { - this.tryEmit(inputDataClone); - } - } - - /** - * @private - * return the state of the recognizer - * the actual recognizing happens in this method - * @virtual - * @param {Object} inputData - * @returns {constant} STATE - */ - - /* jshint ignore:start */ - - }, { - key: 'process', - value: function process(inputData) {} - /* jshint ignore:end */ - - /** - * @private - * return the preferred touch-action - * @virtual - * @returns {Array} - */ - - }, { - key: 'getTouchAction', - value: function getTouchAction() {} - - /** - * @private - * called when the gesture isn't allowed to recognize - * like when another is being recognized or it is disabled - * @virtual - */ - - }, { - key: 'reset', - value: function reset() {} - }]); - return Recognizer; -}(); - -Recognizer.prototype.defaults = {}; - -/** - * @private - * This recognizer is just used as a base for the simple attribute recognizers. - * @constructor - * @extends Recognizer - */ - -var AttrRecognizer = function (_Recognizer) { - inherits(AttrRecognizer, _Recognizer); - - function AttrRecognizer() { - classCallCheck(this, AttrRecognizer); - return possibleConstructorReturn(this, (AttrRecognizer.__proto__ || Object.getPrototypeOf(AttrRecognizer)).apply(this, arguments)); - } - - /** - * @private - * Used to check if it the recognizer receives valid input, like input.distance > 10. - * @memberof AttrRecognizer - * @param {Object} input - * @returns {Boolean} recognized - */ - - - createClass(AttrRecognizer, [{ - key: 'attrTest', - value: function attrTest(input) { - var optionPointers = this.options.pointers; - return optionPointers === 0 || input.pointers.length === optionPointers; - } - - /** - * @private - * Process the input and return the state for the recognizer - * @memberof AttrRecognizer - * @param {Object} input - * @returns {*} State - */ - - }, { - key: 'process', - value: function process(input) { - var state = this.state; - var eventType = input.eventType; - - - var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); - var isValid = this.attrTest(input); - - // on cancel input and we've recognized before, return STATE_CANCELLED - if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { - return state | STATE_CANCELLED; - } else if (isRecognized || isValid) { - if (eventType & INPUT_END) { - return state | STATE_ENDED; - } else if (!(state & STATE_BEGAN)) { - return STATE_BEGAN; - } - return state | STATE_CHANGED; - } - return STATE_FAILED; - } - }]); - return AttrRecognizer; -}(Recognizer); - -AttrRecognizer.prototype.defaults = { - /** - * @private - * @type {Number} - * @default 1 - */ - pointers: 1 -}; - -/** - * @private - * Rotate - * Recognized when two or more pointer are moving in a circular motion. - * @constructor - * @extends AttrRecognizer - */ - -var RotateRecognizer = function (_AttrRecognizer) { - inherits(RotateRecognizer, _AttrRecognizer); - - function RotateRecognizer() { - classCallCheck(this, RotateRecognizer); - return possibleConstructorReturn(this, (RotateRecognizer.__proto__ || Object.getPrototypeOf(RotateRecognizer)).apply(this, arguments)); - } - - createClass(RotateRecognizer, [{ - key: 'getTouchAction', - value: function getTouchAction() { - return [TOUCH_ACTION_NONE]; - } - }, { - key: 'attrTest', - value: function attrTest(input) { - return get(RotateRecognizer.prototype.__proto__ || Object.getPrototypeOf(RotateRecognizer.prototype), 'attrTest', this).call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); - } - }]); - return RotateRecognizer; -}(AttrRecognizer); - -RotateRecognizer.prototype.defaults = { - event: 'rotate', - threshold: 0, - pointers: 2 -}; - -/** - * @private - * Pinch - * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). - * @constructor - * @extends AttrRecognizer - */ - -var PinchRecognizer = function (_AttrRecognizer) { - inherits(PinchRecognizer, _AttrRecognizer); - - function PinchRecognizer() { - classCallCheck(this, PinchRecognizer); - return possibleConstructorReturn(this, (PinchRecognizer.__proto__ || Object.getPrototypeOf(PinchRecognizer)).apply(this, arguments)); - } - - createClass(PinchRecognizer, [{ - key: 'getTouchAction', - value: function getTouchAction() { - return [TOUCH_ACTION_NONE]; - } - }, { - key: 'attrTest', - value: function attrTest(input) { - return get(PinchRecognizer.prototype.__proto__ || Object.getPrototypeOf(PinchRecognizer.prototype), 'attrTest', this).call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); - } - }, { - key: 'emit', - value: function emit(input) { - if (input.scale !== 1) { - var inOut = input.scale < 1 ? 'in' : 'out'; - input.additionalEvent = this.options.event + inOut; - } - get(PinchRecognizer.prototype.__proto__ || Object.getPrototypeOf(PinchRecognizer.prototype), 'emit', this).call(this, input); - } - }]); - return PinchRecognizer; -}(AttrRecognizer); - -PinchRecognizer.prototype.defaults = { - event: 'pinch', - threshold: 0, - pointers: 2 -}; - -/** - * @private - * direction cons to string - * @param {constant} direction - * @returns {String} - */ -function directionStr(direction) { - if (direction === DIRECTION_DOWN) { - return 'down'; - } else if (direction === DIRECTION_UP) { - return 'up'; - } else if (direction === DIRECTION_LEFT) { - return 'left'; - } else if (direction === DIRECTION_RIGHT) { - return 'right'; - } - return ''; -} - -/** - * @private - * Pan - * Recognized when the pointer is down and moved in the allowed direction. - * @constructor - * @extends AttrRecognizer - */ - -var PanRecognizer = function (_AttrRecognizer) { - inherits(PanRecognizer, _AttrRecognizer); - - function PanRecognizer() { - classCallCheck(this, PanRecognizer); - - var _this = possibleConstructorReturn(this, (PanRecognizer.__proto__ || Object.getPrototypeOf(PanRecognizer)).apply(this, arguments)); - - _this.pX = null; - _this.pY = null; - return _this; - } - - createClass(PanRecognizer, [{ - key: 'getTouchAction', - value: function getTouchAction() { - var direction = this.options.direction; - - var actions = []; - if (direction & DIRECTION_HORIZONTAL) { - actions.push(TOUCH_ACTION_PAN_Y); - } - if (direction & DIRECTION_VERTICAL) { - actions.push(TOUCH_ACTION_PAN_X); - } - return actions; - } - }, { - key: 'directionTest', - value: function directionTest(input) { - var options = this.options; - - var hasMoved = true; - var distance = input.distance; - var direction = input.direction; - - var x = input.deltaX; - var y = input.deltaY; - - // lock to axis? - if (!(direction & options.direction)) { - if (options.direction & DIRECTION_HORIZONTAL) { - direction = x === 0 ? DIRECTION_NONE : x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - hasMoved = x !== this.pX; - distance = Math.abs(input.deltaX); - } else { - direction = y === 0 ? DIRECTION_NONE : y < 0 ? DIRECTION_UP : DIRECTION_DOWN; - hasMoved = y !== this.pY; - distance = Math.abs(input.deltaY); - } - } - input.direction = direction; - return hasMoved && distance > options.threshold && direction & options.direction; - } - }, { - key: 'attrTest', - value: function attrTest(input) { - return AttrRecognizer.prototype.attrTest.call(this, input) && ( // replace with a super call - this.state & STATE_BEGAN || !(this.state & STATE_BEGAN) && this.directionTest(input)); - } - }, { - key: 'emit', - value: function emit(input) { - - this.pX = input.deltaX; - this.pY = input.deltaY; - - var direction = directionStr(input.direction); - - if (direction) { - input.additionalEvent = this.options.event + direction; - } - get(PanRecognizer.prototype.__proto__ || Object.getPrototypeOf(PanRecognizer.prototype), 'emit', this).call(this, input); - } - }]); - return PanRecognizer; -}(AttrRecognizer); - -PanRecognizer.prototype.defaults = { - event: 'pan', - threshold: 10, - pointers: 1, - direction: DIRECTION_ALL -}; - -/** - * @private - * Swipe - * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. - * @constructor - * @extends AttrRecognizer - */ - -var SwipeRecognizer = function (_AttrRecognizer) { - inherits(SwipeRecognizer, _AttrRecognizer); - - function SwipeRecognizer() { - classCallCheck(this, SwipeRecognizer); - return possibleConstructorReturn(this, (SwipeRecognizer.__proto__ || Object.getPrototypeOf(SwipeRecognizer)).apply(this, arguments)); - } - - createClass(SwipeRecognizer, [{ - key: 'getTouchAction', - value: function getTouchAction() { - return PanRecognizer.prototype.getTouchAction.call(this); - } - }, { - key: 'attrTest', - value: function attrTest(input) { - var direction = this.options.direction; - - var velocity = void 0; - - if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { - velocity = input.overallVelocity; - } else if (direction & DIRECTION_HORIZONTAL) { - velocity = input.overallVelocityX; - } else if (direction & DIRECTION_VERTICAL) { - velocity = input.overallVelocityY; - } - - return get(SwipeRecognizer.prototype.__proto__ || Object.getPrototypeOf(SwipeRecognizer.prototype), 'attrTest', this).call(this, input) && direction & input.offsetDirection && input.distance > this.options.threshold && input.maxPointers === this.options.pointers && abs(velocity) > this.options.velocity && input.eventType & INPUT_END; - } - }, { - key: 'emit', - value: function emit(input) { - var direction = directionStr(input.offsetDirection); - if (direction) { - this.manager.emit(this.options.event + direction, input); - } - - this.manager.emit(this.options.event, input); - } - }]); - return SwipeRecognizer; -}(AttrRecognizer); - -SwipeRecognizer.prototype.defaults = { - event: 'swipe', - threshold: 10, - velocity: 0.3, - direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, - pointers: 1 -}; - -/** - * @private - * simple function bind - * @param {Function} fn - * @param {Object} context - * @returns {Function} - */ -function bindFn(fn, context) { - return function boundFn() { - return fn.apply(context, arguments); - }; -} - -/** - * @private - * set a timeout with a given scope - * @param {Function} fn - * @param {Number} timeout - * @param {Object} context - * @returns {number} - */ -function setTimeoutContext(fn, timeout, context) { - return setTimeout(bindFn(fn, context), timeout); -} - -/** - * @private - * calculate the absolute distance between two points - * @param {Object} p1 {x, y} - * @param {Object} p2 {x, y} - * @param {Array} [props] containing x and y keys - * @return {Number} distance - */ -function getDistance(p1, p2, props) { - if (!props) { - props = PROPS_XY; - } - var x = p2[props[0]] - p1[props[0]]; - var y = p2[props[1]] - p1[props[1]]; - - return Math.sqrt(x * x + y * y); -} - -/** - * @private - * A tap is recognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur - * between the given interval and position. The delay option can be used to recognize multi-taps without firing - * a single tap. - * - * The eventData from the emitted event contains the property `tapCount`, which contains the amount of - * multi-taps being recognized. - * @constructor - * @extends Recognizer - */ - -var TapRecognizer = function (_Recognizer) { - inherits(TapRecognizer, _Recognizer); - - function TapRecognizer() { - classCallCheck(this, TapRecognizer); - - // previous time and center, - // used for tap counting - var _this = possibleConstructorReturn(this, (TapRecognizer.__proto__ || Object.getPrototypeOf(TapRecognizer)).apply(this, arguments)); - - _this.pTime = false; - _this.pCenter = false; - - _this._timer = null; - _this._input = null; - _this.count = 0; - return _this; - } - - createClass(TapRecognizer, [{ - key: 'getTouchAction', - value: function getTouchAction() { - return [TOUCH_ACTION_MANIPULATION]; - } - }, { - key: 'process', - value: function process(input) { - var _this2 = this; - - var options = this.options; - - - var validPointers = input.pointers.length === options.pointers; - var validMovement = input.distance < options.threshold; - var validTouchTime = input.deltaTime < options.time; - - this.reset(); - - if (input.eventType & INPUT_START && this.count === 0) { - return this.failTimeout(); - } - - // we only allow little movement - // and we've reached an end event, so a tap is possible - if (validMovement && validTouchTime && validPointers) { - if (input.eventType !== INPUT_END) { - return this.failTimeout(); - } - - var validInterval = this.pTime ? input.timeStamp - this.pTime < options.interval : true; - var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; - - this.pTime = input.timeStamp; - this.pCenter = input.center; - - if (!validMultiTap || !validInterval) { - this.count = 1; - } else { - this.count += 1; - } - - this._input = input; - - // if tap count matches we have recognized it, - // else it has began recognizing... - var tapCount = this.count % options.taps; - if (tapCount === 0) { - // no failing requirements, immediately trigger the tap event - // or wait as long as the multitap interval to trigger - if (!this.hasRequireFailures()) { - return STATE_RECOGNIZED; - } else { - this._timer = setTimeoutContext(function () { - _this2.state = STATE_RECOGNIZED; - _this2.tryEmit(); - }, options.interval, this); - return STATE_BEGAN; - } - } - } - return STATE_FAILED; - } - }, { - key: 'failTimeout', - value: function failTimeout() { - var _this3 = this; - - this._timer = setTimeoutContext(function () { - _this3.state = STATE_FAILED; - }, this.options.interval, this); - return STATE_FAILED; - } - }, { - key: 'reset', - value: function reset() { - clearTimeout(this._timer); - } - }, { - key: 'emit', - value: function emit() { - if (this.state === STATE_RECOGNIZED) { - this._input.tapCount = this.count; - this.manager.emit(this.options.event, this._input); - } - } - }]); - return TapRecognizer; -}(Recognizer); - -TapRecognizer.prototype.defaults = { - event: 'tap', - pointers: 1, - taps: 1, - interval: 300, // max time between the multi-tap taps - time: 250, // max time of the pointer to be down (like finger on the screen) - threshold: 9, // a minimal movement is ok, but keep it low - posThreshold: 10 // a multi-tap can be a bit off the initial position -}; - -/** - * @private - * Press - * Recognized when the pointer is down for x ms without any movement. - * @constructor - * @extends Recognizer - */ - -var PressRecognizer = function (_Recognizer) { - inherits(PressRecognizer, _Recognizer); - - function PressRecognizer() { - classCallCheck(this, PressRecognizer); - - var _this = possibleConstructorReturn(this, (PressRecognizer.__proto__ || Object.getPrototypeOf(PressRecognizer)).apply(this, arguments)); - - _this._timer = null; - _this._input = null; - return _this; - } - - createClass(PressRecognizer, [{ - key: 'getTouchAction', - value: function getTouchAction() { - return [TOUCH_ACTION_AUTO]; - } - }, { - key: 'process', - value: function process(input) { - var _this2 = this; - - var options = this.options; - - var validPointers = input.pointers.length === options.pointers; - var validMovement = input.distance < options.threshold; - var validTime = input.deltaTime > options.time; - - this._input = input; - - // we only allow little movement - // and we've reached an end event, so a tap is possible - if (!validMovement || !validPointers || input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime) { - this.reset(); - } else if (input.eventType & INPUT_START) { - this.reset(); - this._timer = setTimeoutContext(function () { - _this2.state = STATE_RECOGNIZED; - _this2.tryEmit(); - }, options.time, this); - } else if (input.eventType & INPUT_END) { - return STATE_RECOGNIZED; - } - return STATE_FAILED; - } - }, { - key: 'reset', - value: function reset() { - clearTimeout(this._timer); - } - }, { - key: 'emit', - value: function emit(input) { - if (this.state !== STATE_RECOGNIZED) { - return; - } - - if (input && input.eventType & INPUT_END) { - this.manager.emit(this.options.event + 'up', input); - } else { - this._input.timeStamp = now(); - this.manager.emit(this.options.event, this._input); - } - } - }]); - return PressRecognizer; -}(Recognizer); - -PressRecognizer.prototype.defaults = { - event: 'press', - pointers: 1, - time: 251, // minimal time of the pointer to be pressed - threshold: 9 // a minimal movement is ok, but keep it low -}; - -/** - * @private - * small indexOf wrapper - * @param {String} str - * @param {String} find - * @returns {Boolean} found - */ -function inStr(str, find) { - return str.indexOf(find) > -1; -} - -/** - * @private - * when the touchActions are collected they are not a valid value, so we need to clean things up. * - * @param {String} actions - * @returns {*} - */ -function cleanTouchActions(actions) { - // none - if (inStr(actions, TOUCH_ACTION_NONE)) { - return TOUCH_ACTION_NONE; - } - - var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); - var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); - - // if both pan-x and pan-y are set (different recognizers - // for different directions, e.g. horizontal pan but vertical swipe?) - // we need none (as otherwise with pan-x pan-y combined none of these - // recognizers will work, since the browser would handle all panning - if (hasPanX && hasPanY) { - return TOUCH_ACTION_NONE; - } - - // pan-x OR pan-y - if (hasPanX || hasPanY) { - return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; - } - - // manipulation - if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { - return TOUCH_ACTION_MANIPULATION; - } - - return TOUCH_ACTION_AUTO; -} - -/** - * @private - * Touch Action - * sets the touchAction property or uses the js alternative - * @param {Manager} manager - * @param {String} value - * @constructor - */ - -var TouchAction = function () { - function TouchAction(manager, value) { - classCallCheck(this, TouchAction); - - this.manager = manager; - this.set(value); - } - - /** - * @private - * set the touchAction value on the element or enable the polyfill - * @param {String} value - */ - - - createClass(TouchAction, [{ - key: 'set', - value: function set(value) { - // find out the touch-action by the event handlers - if (value === TOUCH_ACTION_COMPUTE) { - value = this.compute(); - } - - if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { - this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; - } - this.actions = value.toLowerCase().trim(); - } - - /** - * @private - * just re-set the touchAction value - */ - - }, { - key: 'update', - value: function update() { - this.set(this.manager.options.touchAction); - } - - /** - * @private - * compute the value for the touchAction property based on the recognizer's settings - * @returns {String} value - */ - - }, { - key: 'compute', - value: function compute() { - var actions = []; - each(this.manager.recognizers, function (recognizer) { - if (boolOrFn(recognizer.options.enable, [recognizer])) { - actions = actions.concat(recognizer.getTouchAction()); - } - }); - return cleanTouchActions(actions.join(' ')); - } - - /** - * @private - * this method is called on each input cycle and provides the preventing of the browser behavior - * @param {Object} input - */ - - }, { - key: 'preventDefaults', - value: function preventDefaults(input) { - var srcEvent = input.srcEvent; - - var direction = input.offsetDirection; - - // if the touch action did prevented once this session - if (this.manager.session.prevented) { - srcEvent.preventDefault(); - return; - } - - var actions = this.actions; - - var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; - var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; - var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; - - if (hasNone) { - // do not prevent defaults if this is a tap gesture - var isTapPointer = input.pointers.length === 1; - var isTapMovement = input.distance < 2; - var isTapTouchTime = input.deltaTime < 250; - - if (isTapPointer && isTapMovement && isTapTouchTime) { - return; - } - } - - if (hasPanX && hasPanY) { - // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent - return; - } - - if (hasNone || hasPanY && direction & DIRECTION_HORIZONTAL || hasPanX && direction & DIRECTION_VERTICAL) { - return this.preventSrc(srcEvent); - } - } - - /** - * @private - * call preventDefault to prevent the browser's default behavior (scrolling in most cases) - * @param {Object} srcEvent - */ - - }, { - key: 'preventSrc', - value: function preventSrc(srcEvent) { - this.manager.session.prevented = true; - srcEvent.preventDefault(); - } - }]); - return TouchAction; -}(); - -/** - * @private - * find if a node is in the given parent - * @method hasParent - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @return {Boolean} found - */ -function hasParent(node, parent) { - while (node) { - if (node === parent) { - return true; - } - node = node.parentNode; - } - return false; -} - -/** - * @private - * get the center of all the pointers - * @param {Array} pointers - * @return {Object} center contains `x` and `y` properties - */ -function getCenter(pointers) { - var pointersLength = pointers.length; - - // no need to loop when only one touch - if (pointersLength === 1) { - return { - x: round(pointers[0].clientX), - y: round(pointers[0].clientY) - }; - } - - var x = 0; - var y = 0; - var i = 0; - while (i < pointersLength) { - x += pointers[i].clientX; - y += pointers[i].clientY; - i++; - } - - return { - x: round(x / pointersLength), - y: round(y / pointersLength) - }; -} - -/** - * @private - * create a simple clone from the input used for storage of firstInput and firstMultiple - * @param {Object} input - * @returns {Object} clonedInputData - */ -function simpleCloneInputData(input) { - // make a simple copy of the pointers because we will get a reference if we don't - // we only need clientXY for the calculations - var pointers = []; - var i = 0; - while (i < input.pointers.length) { - pointers[i] = { - clientX: round(input.pointers[i].clientX), - clientY: round(input.pointers[i].clientY) - }; - i++; - } - - return { - timeStamp: now(), - pointers: pointers, - center: getCenter(pointers), - deltaX: input.deltaX, - deltaY: input.deltaY - }; -} - -/** - * @private - * calculate the angle between two coordinates - * @param {Object} p1 - * @param {Object} p2 - * @param {Array} [props] containing x and y keys - * @return {Number} angle - */ -function getAngle(p1, p2, props) { - if (!props) { - props = PROPS_XY; - } - var x = p2[props[0]] - p1[props[0]]; - var y = p2[props[1]] - p1[props[1]]; - return Math.atan2(y, x) * 180 / Math.PI; -} - -/** - * @private - * get the direction between two points - * @param {Number} x - * @param {Number} y - * @return {Number} direction - */ -function getDirection(x, y) { - if (x === y) { - return DIRECTION_NONE; - } - - if (abs(x) >= abs(y)) { - return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; -} - -function computeDeltaXY(session, input) { - var center = input.center; - // let { offsetDelta:offset = {}, prevDelta = {}, prevInput = {} } = session; - // jscs throwing error on defalut destructured values and without defaults tests fail - - var offset = session.offsetDelta || {}; - var prevDelta = session.prevDelta || {}; - var prevInput = session.prevInput || {}; - - if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { - prevDelta = session.prevDelta = { - x: prevInput.deltaX || 0, - y: prevInput.deltaY || 0 - }; - - offset = session.offsetDelta = { - x: center.x, - y: center.y - }; - } - - input.deltaX = prevDelta.x + (center.x - offset.x); - input.deltaY = prevDelta.y + (center.y - offset.y); -} - -/** - * @private - * calculate the velocity between two points. unit is in px per ms. - * @param {Number} deltaTime - * @param {Number} x - * @param {Number} y - * @return {Object} velocity `x` and `y` - */ -function getVelocity(deltaTime, x, y) { - return { - x: x / deltaTime || 0, - y: y / deltaTime || 0 - }; -} - -/** - * @private - * calculate the scale factor between two pointersets - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @param {Array} start array of pointers - * @param {Array} end array of pointers - * @return {Number} scale - */ -function getScale(start, end) { - return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); -} - -/** - * @private - * calculate the rotation degrees between two pointersets - * @param {Array} start array of pointers - * @param {Array} end array of pointers - * @return {Number} rotation - */ -function getRotation(start, end) { - return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); -} - -/** - * @private - * velocity is calculated every x ms - * @param {Object} session - * @param {Object} input - */ -function computeIntervalInputData(session, input) { - var last = session.lastInterval || input; - var deltaTime = input.timeStamp - last.timeStamp; - var velocity = void 0; - var velocityX = void 0; - var velocityY = void 0; - var direction = void 0; - - if (input.eventType !== INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { - var deltaX = input.deltaX - last.deltaX; - var deltaY = input.deltaY - last.deltaY; - - var v = getVelocity(deltaTime, deltaX, deltaY); - velocityX = v.x; - velocityY = v.y; - velocity = abs(v.x) > abs(v.y) ? v.x : v.y; - direction = getDirection(deltaX, deltaY); - - session.lastInterval = input; - } else { - // use latest velocity info if it doesn't overtake a minimum period - velocity = last.velocity; - velocityX = last.velocityX; - velocityY = last.velocityY; - direction = last.direction; - } - - input.velocity = velocity; - input.velocityX = velocityX; - input.velocityY = velocityY; - input.direction = direction; -} - -/** -* @private - * extend the data with some usable properties like scale, rotate, velocity etc - * @param {Object} manager - * @param {Object} input - */ -function computeInputData(manager, input) { - var session = manager.session; - var pointers = input.pointers; - var pointersLength = pointers.length; - - // store the first input to calculate the distance and direction - - if (!session.firstInput) { - session.firstInput = simpleCloneInputData(input); - } - - // to compute scale and rotation we need to store the multiple touches - if (pointersLength > 1 && !session.firstMultiple) { - session.firstMultiple = simpleCloneInputData(input); - } else if (pointersLength === 1) { - session.firstMultiple = false; - } - - var firstInput = session.firstInput; - var firstMultiple = session.firstMultiple; - - var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; - - var center = input.center = getCenter(pointers); - input.timeStamp = now(); - input.deltaTime = input.timeStamp - firstInput.timeStamp; - - input.angle = getAngle(offsetCenter, center); - input.distance = getDistance(offsetCenter, center); - - computeDeltaXY(session, input); - input.offsetDirection = getDirection(input.deltaX, input.deltaY); - - var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); - input.overallVelocityX = overallVelocity.x; - input.overallVelocityY = overallVelocity.y; - input.overallVelocity = abs(overallVelocity.x) > abs(overallVelocity.y) ? overallVelocity.x : overallVelocity.y; - - input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; - input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; - - input.maxPointers = !session.prevInput ? input.pointers.length : input.pointers.length > session.prevInput.maxPointers ? input.pointers.length : session.prevInput.maxPointers; - - computeIntervalInputData(session, input); - - // find the correct target - var target = manager.element; - if (hasParent(input.srcEvent.target, target)) { - target = input.srcEvent.target; - } - input.target = target; -} - -/** - * @private - * handle input events - * @param {Manager} manager - * @param {String} eventType - * @param {Object} input - */ -function inputHandler(manager, eventType, input) { - var pointersLen = input.pointers.length; - var changedPointersLen = input.changedPointers.length; - var isFirst = eventType & INPUT_START && pointersLen - changedPointersLen === 0; - var isFinal = eventType & (INPUT_END | INPUT_CANCEL) && pointersLen - changedPointersLen === 0; - - input.isFirst = !!isFirst; - input.isFinal = !!isFinal; - - if (isFirst) { - manager.session = {}; - } - - // source event is the normalized value of the domEvents - // like 'touchstart, mouseup, pointerdown' - input.eventType = eventType; - - // compute scale, rotation etc - computeInputData(manager, input); - - // emit secret event - manager.emit('hammer.input', input); - - manager.recognize(input); - manager.session.prevInput = input; -} - -/** - * @private - * split string on whitespace - * @param {String} str - * @returns {Array} words - */ - -function splitStr(str) { - return str.trim().split(/\s+/g); -} - -/** - * @private - * addEventListener with multiple events at once - * @param {EventTarget} target - * @param {String} types - * @param {Function} handler - */ -function addEventListeners(target, types, handler) { - each(splitStr(types), function (type) { - target.addEventListener(type, handler, false); - }); -} - -/** - * @private - * removeEventListener with multiple events at once - * @param {EventTarget} target - * @param {String} types - * @param {Function} handler - */ -function removeEventListeners(target, types, handler) { - each(splitStr(types), function (type) { - target.removeEventListener(type, handler, false); - }); -} - -/** - * @private - * get the window object of an element - * @param {HTMLElement} element - * @returns {DocumentView|Window} - */ -function getWindowForElement(element) { - var doc = element.ownerDocument || element; - return doc.defaultView || doc.parentWindow || window; -} - -/** - * @private - * create new input type manager - * @param {Manager} manager - * @param {Function} callback - * @returns {Input} - * @constructor - */ - -var Input = function () { - function Input(manager, callback) { - classCallCheck(this, Input); - - var self = this; - this.manager = manager; - this.callback = callback; - this.element = manager.element; - this.target = manager.options.inputTarget; - - // smaller wrapper around the handler, for the scope and the enabled state of the manager, - // so when disabled the input events are completely bypassed. - this.domHandler = function (ev) { - if (boolOrFn(manager.options.enable, [manager])) { - self.handler(ev); - } - }; - - this.init(); - } - /** - * @private - * should handle the inputEvent data and trigger the callback - * @virtual - */ - - - createClass(Input, [{ - key: 'handler', - value: function handler() {} - - /** - * @private - * bind the events - */ - - }, { - key: 'init', - value: function init() { - this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); - this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); - this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); - } - - /** - * @private - * unbind the events - */ - - }, { - key: 'destroy', - value: function destroy() { - this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); - this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); - this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); - } - }]); - return Input; -}(); - -var POINTER_INPUT_MAP = { - pointerdown: INPUT_START, - pointermove: INPUT_MOVE, - pointerup: INPUT_END, - pointercancel: INPUT_CANCEL, - pointerout: INPUT_CANCEL -}; - -// in IE10 the pointer types is defined as an enum -var IE10_POINTER_TYPE_ENUM = { - 2: INPUT_TYPE_TOUCH, - 3: INPUT_TYPE_PEN, - 4: INPUT_TYPE_MOUSE, - 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 -}; - -var POINTER_ELEMENT_EVENTS = 'pointerdown'; -var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; - -// IE10 has prefixed support, and case-sensitive -if (window.MSPointerEvent && !window.PointerEvent) { - POINTER_ELEMENT_EVENTS = 'MSPointerDown'; - POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; -} - -/** - * @private - * Pointer events input - * @constructor - * @extends Input - */ - -var PointerEventInput = function (_Input) { - inherits(PointerEventInput, _Input); - - function PointerEventInput() { - classCallCheck(this, PointerEventInput); - - var _this = possibleConstructorReturn(this, (PointerEventInput.__proto__ || Object.getPrototypeOf(PointerEventInput)).apply(this, arguments)); - - _this.evEl = POINTER_ELEMENT_EVENTS; - _this.evWin = POINTER_WINDOW_EVENTS; - - _this.store = _this.manager.session.pointerEvents = []; - return _this; - } - - /** - * @private - * handle mouse events - * @param {Object} ev - */ - - - createClass(PointerEventInput, [{ - key: 'handler', - value: function handler(ev) { - var store = this.store; - - var removePointer = false; - - var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); - var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; - var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; - - var isTouch = pointerType === INPUT_TYPE_TOUCH; - - // get index of the event in the store - var storeIndex = inArray(store, ev.pointerId, 'pointerId'); - - // start and mouse must be down - if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { - if (storeIndex < 0) { - store.push(ev); - storeIndex = store.length - 1; - } - } else if (eventType & (INPUT_END | INPUT_CANCEL)) { - removePointer = true; - } - - // it not found, so the pointer hasn't been down (so it's probably a hover) - if (storeIndex < 0) { - return; - } - - // update the event in the store - store[storeIndex] = ev; - - this.callback(this.manager, eventType, { - pointers: store, - changedPointers: [ev], - pointerType: pointerType, - srcEvent: ev - }); - - if (removePointer) { - // remove from the store - store.splice(storeIndex, 1); - } - } - }]); - return PointerEventInput; -}(Input); - -/** - * @private - * convert array-like objects to real arrays - * @param {Object} obj - * @returns {Array} - */ -function toArray$1(obj) { - return Array.prototype.slice.call(obj, 0); -} - -/** - * @private - * unique array with objects based on a key (like 'id') or just by the array's value - * @param {Array} src [{id:1},{id:2},{id:1}] - * @param {String} [key] - * @param {Boolean} [sort=False] - * @returns {Array} [{id:1},{id:2}] - */ -function uniqueArray(src, key, sort) { - var results = []; - var values = []; - var i = 0; - - while (i < src.length) { - var val = key ? src[i][key] : src[i]; - if (inArray(values, val) < 0) { - results.push(src[i]); - } - values[i] = val; - i++; - } - - if (sort) { - if (!key) { - results = results.sort(); - } else { - results = results.sort(function (a, b) { - return a[key] > b[key]; - }); - } - } - - return results; -} - -var TOUCH_INPUT_MAP = { - touchstart: INPUT_START, - touchmove: INPUT_MOVE, - touchend: INPUT_END, - touchcancel: INPUT_CANCEL -}; - -var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; - -/** - * @private - * Multi-user touch events input - * @constructor - * @extends Input - */ - -var TouchInput = function (_Input) { - inherits(TouchInput, _Input); - - function TouchInput() { - classCallCheck(this, TouchInput); - - TouchInput.prototype.evTarget = TOUCH_TARGET_EVENTS; - TouchInput.prototype.targetIds = {}; - - var _this = possibleConstructorReturn(this, (TouchInput.__proto__ || Object.getPrototypeOf(TouchInput)).apply(this, arguments)); - - _this.evTarget = TOUCH_TARGET_EVENTS; - _this.targetIds = {}; - return _this; - } - - createClass(TouchInput, [{ - key: 'handler', - value: function handler(ev) { - var type = TOUCH_INPUT_MAP[ev.type]; - var touches = getTouches.call(this, ev, type); - if (!touches) { - return; - } - - this.callback(this.manager, type, { - pointers: touches[0], - changedPointers: touches[1], - pointerType: INPUT_TYPE_TOUCH, - srcEvent: ev - }); - } - }]); - return TouchInput; -}(Input); - -function getTouches(ev, type) { - var allTouches = toArray$1(ev.touches); - var targetIds = this.targetIds; - - // when there is only one touch, the process can be simplified - - if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { - targetIds[allTouches[0].identifier] = true; - return [allTouches, allTouches]; - } - - var i = void 0; - var targetTouches = void 0; - var changedTouches = toArray$1(ev.changedTouches); - var changedTargetTouches = []; - var target = this.target; - - // get target touches from touches - - targetTouches = allTouches.filter(function (touch) { - return hasParent(touch.target, target); - }); - - // collect touches - if (type === INPUT_START) { - i = 0; - while (i < targetTouches.length) { - targetIds[targetTouches[i].identifier] = true; - i++; - } - } - - // filter changed touches to only contain touches that exist in the collected target ids - i = 0; - while (i < changedTouches.length) { - if (targetIds[changedTouches[i].identifier]) { - changedTargetTouches.push(changedTouches[i]); - } - - // cleanup removed touches - if (type & (INPUT_END | INPUT_CANCEL)) { - delete targetIds[changedTouches[i].identifier]; - } - i++; - } - - if (!changedTargetTouches.length) { - return; - } - - return [ - // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' - uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), changedTargetTouches]; -} - -var MOUSE_INPUT_MAP = { - mousedown: INPUT_START, - mousemove: INPUT_MOVE, - mouseup: INPUT_END -}; - -var MOUSE_ELEMENT_EVENTS = 'mousedown'; -var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; - -/** - * @private - * Mouse events input - * @constructor - * @extends Input - */ - -var MouseInput = function (_Input) { - inherits(MouseInput, _Input); - - function MouseInput() { - classCallCheck(this, MouseInput); - - var _this = possibleConstructorReturn(this, (MouseInput.__proto__ || Object.getPrototypeOf(MouseInput)).apply(this, arguments)); - - _this.evEl = MOUSE_ELEMENT_EVENTS; - _this.evWin = MOUSE_WINDOW_EVENTS; - - _this.pressed = false; // mousedown state - return _this; - } - - /** - * @private - * handle mouse events - * @param {Object} ev - */ - - - createClass(MouseInput, [{ - key: 'handler', - value: function handler(ev) { - var eventType = MOUSE_INPUT_MAP[ev.type]; - - // on start we want to have the left mouse button down - if (eventType & INPUT_START && ev.button === 0) { - this.pressed = true; - } - - if (eventType & INPUT_MOVE && ev.which !== 1) { - eventType = INPUT_END; - } - - // mouse must be down - if (!this.pressed) { - return; - } - - if (eventType & INPUT_END) { - this.pressed = false; - } - - this.callback(this.manager, eventType, { - pointers: [ev], - changedPointers: [ev], - pointerType: INPUT_TYPE_MOUSE, - srcEvent: ev - }); - } - }]); - return MouseInput; -}(Input); - -/** - * @private - * Combined touch and mouse input - * - * Touch has a higher priority then mouse, and while touching no mouse events are allowed. - * This because touch devices also emit mouse events while doing a touch. - * - * @constructor - * @extends Input - */ - -var DEDUP_TIMEOUT = 2500; -var DEDUP_DISTANCE = 25; - -var TouchMouseInput = function (_Input) { - inherits(TouchMouseInput, _Input); - - function TouchMouseInput() { - classCallCheck(this, TouchMouseInput); - - var _this = possibleConstructorReturn(this, (TouchMouseInput.__proto__ || Object.getPrototypeOf(TouchMouseInput)).apply(this, arguments)); - - var handler = bindFn(_this.handler, _this); - _this.touch = new TouchInput(_this.manager, handler); - _this.mouse = new MouseInput(_this.manager, handler); - - _this.primaryTouch = null; - _this.lastTouches = []; - return _this; - } - - /** - * @private - * handle mouse and touch events - * @param {Hammer} manager - * @param {String} inputEvent - * @param {Object} inputData - */ - - - createClass(TouchMouseInput, [{ - key: 'handler', - value: function handler(manager, inputEvent, inputData) { - var isTouch = inputData.pointerType === INPUT_TYPE_TOUCH; - var isMouse = inputData.pointerType === INPUT_TYPE_MOUSE; - - if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { - return; - } - - // when we're in a touch event, record touches to de-dupe synthetic mouse event - if (isTouch) { - recordTouches.call(this, inputEvent, inputData); - } else if (isMouse && isSyntheticEvent.call(this, inputData)) { - return; - } - - this.callback(manager, inputEvent, inputData); - } - - /** - * @private - * remove the event listeners - */ - - }, { - key: 'destroy', - value: function destroy() { - this.touch.destroy(); - this.mouse.destroy(); - } - }]); - return TouchMouseInput; -}(Input); - -function recordTouches(eventType, eventData) { - if (eventType & INPUT_START) { - this.primaryTouch = eventData.changedPointers[0].identifier; - setLastTouch.call(this, eventData); - } else if (eventType & (INPUT_END | INPUT_CANCEL)) { - setLastTouch.call(this, eventData); - } -} - -function setLastTouch(eventData) { - var _this2 = this; - - var _eventData$changedPoi = slicedToArray(eventData.changedPointers, 1); - - var touch = _eventData$changedPoi[0]; - - if (touch.identifier === this.primaryTouch) { - (function () { - var lastTouch = { x: touch.clientX, y: touch.clientY }; - _this2.lastTouches.push(lastTouch); - var lts = _this2.lastTouches; - var removeLastTouch = function removeLastTouch() { - var i = lts.indexOf(lastTouch); - if (i > -1) { - lts.splice(i, 1); - } - }; - setTimeout(removeLastTouch, DEDUP_TIMEOUT); - })(); - } -} - -function isSyntheticEvent(eventData) { - var x = eventData.srcEvent.clientX; - var y = eventData.srcEvent.clientY; - for (var i = 0; i < this.lastTouches.length; i++) { - var t = this.lastTouches[i]; - var dx = Math.abs(x - t.x); - var dy = Math.abs(y - t.y); - if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { - return true; - } - } - return false; -} - -/** - * @private - * create new input type manager - * called by the Manager constructor - * @param {Hammer} manager - * @returns {Input} - */ -function createInputInstance(manager) { - var Type = void 0; - // let inputClass = manager.options.inputClass; - var inputClass = manager.options.inputClass; - - if (inputClass) { - Type = inputClass; - } else if (SUPPORT_POINTER_EVENTS) { - Type = PointerEventInput; - } else if (SUPPORT_ONLY_TOUCH) { - Type = TouchInput; - } else if (!SUPPORT_TOUCH) { - Type = MouseInput; - } else { - Type = TouchMouseInput; - } - return new Type(manager, inputHandler); -} - -var STOP = 1; -var FORCED_STOP = 2; - -/** -* @private - * Manager - * @param {HTMLElement} element - * @param {Object} [options] - * @constructor - */ - -var Manager = function () { - function Manager(element, options) { - var _this = this; - - classCallCheck(this, Manager); - - this.options = assign$1({}, Hammer.defaults, options || {}); - - this.options.inputTarget = this.options.inputTarget || element; - - this.handlers = {}; - this.session = {}; - this.recognizers = []; - this.oldCssProps = {}; - - this.element = element; - this.input = createInputInstance(this); - this.touchAction = new TouchAction(this, this.options.touchAction); - - toggleCssProps(this, true); - - each(this.options.recognizers, function (item) { - var recognizer = _this.add(new item[0](item[1])); - item[2] && recognizer.recognizeWith(item[2]); - item[3] && recognizer.requireFailure(item[3]); - }, this); - } - - /** - * @private - * set options - * @param {Object} options - * @returns {Manager} - */ - - - createClass(Manager, [{ - key: 'set', - value: function set(options) { - assign$1(this.options, options); - - // Options that need a little more setup - if (options.touchAction) { - this.touchAction.update(); - } - if (options.inputTarget) { - // Clean up existing event listeners and reinitialize - this.input.destroy(); - this.input.target = options.inputTarget; - this.input.init(); - } - return this; - } - - /** - * @private - * stop recognizing for this session. - * This session will be discarded, when a new [input]start event is fired. - * When forced, the recognizer cycle is stopped immediately. - * @param {Boolean} [force] - */ - - }, { - key: 'stop', - value: function stop(force) { - this.session.stopped = force ? FORCED_STOP : STOP; - } - - /** - * @private - * run the recognizers! - * called by the inputHandler function on every movement of the pointers (touches) - * it walks through all the recognizers and tries to detect the gesture that is being made - * @param {Object} inputData - */ - - }, { - key: 'recognize', - value: function recognize(inputData) { - var session = this.session; - - if (session.stopped) { - return; - } - - // run the touch-action polyfill - this.touchAction.preventDefaults(inputData); - - var recognizer = void 0; - var recognizers = this.recognizers; - - // this holds the recognizer that is being recognized. - // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED - // if no recognizer is detecting a thing, it is set to `null` - - var curRecognizer = session.curRecognizer; - - // reset when the last recognizer is recognized - // or when we're in a new session - - if (!curRecognizer || curRecognizer && curRecognizer.state & STATE_RECOGNIZED) { - curRecognizer = session.curRecognizer = null; - } - - var i = 0; - while (i < recognizers.length) { - recognizer = recognizers[i]; - - // find out if we are allowed try to recognize the input for this one. - // 1. allow if the session is NOT forced stopped (see the .stop() method) - // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one - // that is being recognized. - // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. - // this can be setup with the `recognizeWith()` method on the recognizer. - if (session.stopped !== FORCED_STOP && ( // 1 - !curRecognizer || recognizer === curRecognizer || // 2 - recognizer.canRecognizeWith(curRecognizer))) { - // 3 - recognizer.recognize(inputData); - } else { - recognizer.reset(); - } - - // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the - // current active recognizer. but only if we don't already have an active recognizer - if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { - curRecognizer = session.curRecognizer = recognizer; - } - i++; - } - } - - /** - * @private - * get a recognizer by its event name. - * @param {Recognizer|String} recognizer - * @returns {Recognizer|Null} - */ - - }, { - key: 'get', - value: function get(recognizer) { - if (recognizer instanceof Recognizer) { - return recognizer; - } - - var recognizers = this.recognizers; - - for (var i = 0; i < recognizers.length; i++) { - if (recognizers[i].options.event === recognizer) { - return recognizers[i]; - } - } - return null; - } - - /** - * @private add a recognizer to the manager - * existing recognizers with the same event name will be removed - * @param {Recognizer} recognizer - * @returns {Recognizer|Manager} - */ - - }, { - key: 'add', - value: function add(recognizer) { - if (invokeArrayArg(recognizer, 'add', this)) { - return this; - } - - // remove existing - var existing = this.get(recognizer.options.event); - if (existing) { - this.remove(existing); - } - - this.recognizers.push(recognizer); - recognizer.manager = this; - - this.touchAction.update(); - return recognizer; - } - - /** - * @private - * remove a recognizer by name or instance - * @param {Recognizer|String} recognizer - * @returns {Manager} - */ - - }, { - key: 'remove', - value: function remove(recognizer) { - if (invokeArrayArg(recognizer, 'remove', this)) { - return this; - } - - recognizer = this.get(recognizer); - - // let's make sure this recognizer exists - if (recognizer) { - var recognizers = this.recognizers; - - var index = inArray(recognizers, recognizer); - - if (index !== -1) { - recognizers.splice(index, 1); - this.touchAction.update(); - } - } - - return this; - } - - /** - * @private - * bind event - * @param {String} events - * @param {Function} handler - * @returns {EventEmitter} this - */ - - }, { - key: 'on', - value: function on(events, handler) { - if (events === undefined) { - return; - } - if (handler === undefined) { - return; - } - - var handlers = this.handlers; - - each(splitStr(events), function (event) { - handlers[event] = handlers[event] || []; - handlers[event].push(handler); - }); - return this; - } - - /** - * @private unbind event, leave emit blank to remove all handlers - * @param {String} events - * @param {Function} [handler] - * @returns {EventEmitter} this - */ - - }, { - key: 'off', - value: function off(events, handler) { - if (events === undefined) { - return; - } - - var handlers = this.handlers; - - each(splitStr(events), function (event) { - if (!handler) { - delete handlers[event]; - } else { - handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); - } - }); - return this; - } - - /** - * @private emit event to the listeners - * @param {String} event - * @param {Object} data - */ - - }, { - key: 'emit', - value: function emit(event, data) { - // we also want to trigger dom events - if (this.options.domEvents) { - triggerDomEvent(event, data); - } - - // no handlers, so skip it all - var handlers = this.handlers[event] && this.handlers[event].slice(); - if (!handlers || !handlers.length) { - return; - } - - data.type = event; - data.preventDefault = function () { - data.srcEvent.preventDefault(); - }; - - var i = 0; - while (i < handlers.length) { - handlers[i](data); - i++; - } - } - - /** - * @private - * destroy the manager and unbinds all events - * it doesn't unbind dom events, that is the user own responsibility - */ - - }, { - key: 'destroy', - value: function destroy() { - this.element && toggleCssProps(this, false); - - this.handlers = {}; - this.session = {}; - this.input.destroy(); - this.element = null; - } - }]); - return Manager; -}(); - -function toggleCssProps(manager, add) { - var element = manager.element; - - if (!element.style) { - return; - } - var prop = void 0; - each(manager.options.cssProps, function (value, name) { - prop = prefixed(element.style, name); - if (add) { - manager.oldCssProps[prop] = element.style[prop]; - element.style[prop] = value; - } else { - element.style[prop] = manager.oldCssProps[prop] || ''; - } - }); - if (!add) { - manager.oldCssProps = {}; - } -} - -/** - * @private - * trigger dom event - * @param {String} event - * @param {Object} data - */ -function triggerDomEvent(event, data) { - var gestureEvent = document.createEvent('Event'); - gestureEvent.initEvent(event, true, true); - gestureEvent.gesture = data; - data.target.dispatchEvent(gestureEvent); -} - -/** - * @private - * Simple way to create a manager with a default set of recognizers. - * @param {HTMLElement} element - * @param {Object} [options] - * @constructor - */ - -var Hammer = function Hammer(element, options) { - classCallCheck(this, Hammer); - - options = options || {}; - options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); - return new Manager(element, options); -}; - -Hammer.VERSION = '2.0.8'; - -/** - * @private - * default settings - * @namespace - */ -Hammer.defaults = { - /** - * @private - * set if DOM events are being triggered. - * But this is slower and unused by simple implementations, so disabled by default. - * @type {Boolean} - * @default false - */ - domEvents: false, - - /** - * @private - * The value for the touchAction property/fallback. - * When set to `compute` it will magically set the correct value based on the added recognizers. - * @type {String} - * @default compute - */ - touchAction: TOUCH_ACTION_COMPUTE, - - /** - * @private - * @type {Boolean} - * @default true - */ - enable: true, - - /** - * @private - * EXPERIMENTAL FEATURE -- can be removed/changed - * Change the parent input target element. - * If Null, then it is being set the to main element. - * @type {Null|EventTarget} - * @default null - */ - inputTarget: null, - - /** - * @private - * force an input class - * @type {Null|Function} - * @default null - */ - inputClass: null, - - /** - * @private - * Default recognizer setup when calling `Hammer()` - * When creating a new Manager these will be skipped. - * @type {Array} - */ - preset: [ - // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] - [RotateRecognizer, { enable: false }], [PinchRecognizer, { enable: false }, ['rotate']], [SwipeRecognizer, { direction: DIRECTION_HORIZONTAL }], [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], [TapRecognizer], [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], [PressRecognizer]], - - /** - * @private - * Some CSS properties can be used to improve the working of Hammer. - * Add them to this method and they will be set when creating a new Manager. - * @namespace - */ - cssProps: { - /** - * @private - * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. - * @type {String} - * @default 'none' - */ - userSelect: 'none', - - /** - * @private - * Disable the Windows Phone grippers when pressing an element. - * @type {String} - * @default 'none' - */ - touchSelect: 'none', - - /** - * @private - * Disables the default callout shown when you touch and hold a touch target. - * On iOS, when you touch and hold a touch target such as a link, Safari displays - * a callout containing information about the link. This property allows you to disable that callout. - * @type {String} - * @default 'none' - */ - touchCallout: 'none', - - /** - * @private - * Specifies whether zooming is enabled. Used by IE10> - * @type {String} - * @default 'none' - */ - contentZooming: 'none', - - /** - * @private - * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. - * @type {String} - * @default 'none' - */ - userDrag: 'none', - - /** - * @private - * Overrides the highlight color shown when the user taps a link or a JavaScript - * clickable element in iOS. This property obeys the alpha value, if specified. - * @type {String} - * @default 'rgba(0,0,0,0)' - */ - tapHighlightColor: 'rgba(0,0,0,0)' - } -}; - -var SINGLE_TOUCH_INPUT_MAP = { - touchstart: INPUT_START, - touchmove: INPUT_MOVE, - touchend: INPUT_END, - touchcancel: INPUT_CANCEL -}; - -var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; -var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; - -/** - * @private - * Touch events input - * @constructor - * @extends Input - */ - -var SingleTouchInput = function (_Input) { - inherits(SingleTouchInput, _Input); - - function SingleTouchInput() { - classCallCheck(this, SingleTouchInput); - - var _this = possibleConstructorReturn(this, (SingleTouchInput.__proto__ || Object.getPrototypeOf(SingleTouchInput)).apply(this, arguments)); - - _this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; - _this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; - _this.started = false; - - Input.apply(_this, arguments); - return _this; - } - - createClass(SingleTouchInput, [{ - key: 'handler', - value: function handler(ev) { - var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; - - // should we handle the touch events? - if (type === INPUT_START) { - this.started = true; - } - - if (!this.started) { - return; - } - - var touches = normalizeSingleTouches.call(this, ev, type); - - // when done, reset the started state - if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { - this.started = false; - } - - this.callback(this.manager, type, { - pointers: touches[0], - changedPointers: touches[1], - pointerType: INPUT_TYPE_TOUCH, - srcEvent: ev - }); - } - }]); - return SingleTouchInput; -}(Input); - -function normalizeSingleTouches(ev, type) { - var all = toArray$1(ev.touches); - var changed = toArray$1(ev.changedTouches); - - if (type & (INPUT_END | INPUT_CANCEL)) { - all = uniqueArray(all.concat(changed), 'identifier', true); - } - - return [all, changed]; -} - -/** - * @private - * wrap a method with a deprecation warning and stack trace - * @param {Function} method - * @param {String} name - * @param {String} message - * @returns {Function} A new function wrapping the supplied method. - */ -function deprecate(method, name, message) { - var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; - return function () { - var e = new Error('get-stack-trace'); - var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '').replace(/^\s+at\s+/gm, '').replace(/^Object.\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; - - var log = window.console && (window.console.warn || window.console.log); - if (log) { - log.call(window.console, deprecationMessage, stack); - } - return method.apply(this, arguments); - }; -} - -/** - * @private - * extend object. - * means that properties in dest will be overwritten by the ones in src. - * @param {Object} dest - * @param {Object} src - * @param {Boolean} [merge=false] - * @returns {Object} dest - */ -var extend = deprecate(function (dest, src, merge) { - var keys = Object.keys(src); - var i = 0; - while (i < keys.length) { - if (!merge || merge && dest[keys[i]] === undefined) { - dest[keys[i]] = src[keys[i]]; - } - i++; - } - return dest; -}, 'extend', 'Use `assign`.'); - -/** - * @private - * merge the values from src in the dest. - * means that properties that exist in dest will not be overwritten by src - * @param {Object} dest - * @param {Object} src - * @returns {Object} dest - */ -var merge = deprecate(function (dest, src) { - return extend(dest, src, true); -}, 'merge', 'Use `assign`.'); - -/** - * @private - * simple class inheritance - * @param {Function} child - * @param {Function} base - * @param {Object} [properties] - */ -function inherit(child, base, properties) { - var baseP = base.prototype; - var childP = void 0; - - childP = child.prototype = Object.create(baseP); - childP.constructor = child; - childP._super = baseP; - - if (properties) { - assign$1(childP, properties); - } -} - -// this prevents errors when Hammer is loaded in the presence of an AMD -// style loader but by script tag, not by the loader. - -assign$1(Hammer, { - INPUT_START: INPUT_START, - INPUT_MOVE: INPUT_MOVE, - INPUT_END: INPUT_END, - INPUT_CANCEL: INPUT_CANCEL, - - STATE_POSSIBLE: STATE_POSSIBLE, - STATE_BEGAN: STATE_BEGAN, - STATE_CHANGED: STATE_CHANGED, - STATE_ENDED: STATE_ENDED, - STATE_RECOGNIZED: STATE_RECOGNIZED, - STATE_CANCELLED: STATE_CANCELLED, - STATE_FAILED: STATE_FAILED, - - DIRECTION_NONE: DIRECTION_NONE, - DIRECTION_LEFT: DIRECTION_LEFT, - DIRECTION_RIGHT: DIRECTION_RIGHT, - DIRECTION_UP: DIRECTION_UP, - DIRECTION_DOWN: DIRECTION_DOWN, - DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, - DIRECTION_VERTICAL: DIRECTION_VERTICAL, - DIRECTION_ALL: DIRECTION_ALL, - - Manager: Manager, - Input: Input, - TouchAction: TouchAction, - - TouchInput: TouchInput, - MouseInput: MouseInput, - PointerEventInput: PointerEventInput, - TouchMouseInput: TouchMouseInput, - SingleTouchInput: SingleTouchInput, - - Recognizer: Recognizer, - AttrRecognizer: AttrRecognizer, - Tap: TapRecognizer, - Pan: PanRecognizer, - Swipe: SwipeRecognizer, - Pinch: PinchRecognizer, - Rotate: RotateRecognizer, - Press: PressRecognizer, - - on: addEventListeners, - off: removeEventListeners, - each: each, - merge: merge, - extend: extend, - assign: assign$1, - inherit: inherit, - bindFn: bindFn, - prefixed: prefixed, - toArray: toArray$1, - inArray: inArray, - uniqueArray: uniqueArray, - splitStr: splitStr, - boolOrFn: boolOrFn, - hasParent: hasParent, - addEventListeners: addEventListeners, - removeEventListeners: removeEventListeners -}); - -/* jshint ignore:start */ -if (typeof define === 'function' && define.amd) { - define(function () { - return Hammer; - }); -} else if (typeof module !== 'undefined' && module.exports) { - module.exports = Hammer; -} else { - window[exportName] = Hammer; -} -/* jshint ignore:end */ -})(window, document, 'Hammer'); \ No newline at end of file diff --git a/Packages/ohif-cornerstone/client/index.js b/Packages/ohif-cornerstone/client/index.js index 63f94473a..3d4a0ca7e 100644 --- a/Packages/ohif-cornerstone/client/index.js +++ b/Packages/ohif-cornerstone/client/index.js @@ -1,4 +1,4 @@ -import { Cornerstone } from '../namespace'; +import { OHIF } from '../namespace'; import { MetadataProvider } from './lib/classes/MetadataProvider'; -Cornerstone.metadataProvider = new MetadataProvider(); \ No newline at end of file +OHIF.cornerstone.metadataProvider = new MetadataProvider(); \ No newline at end of file diff --git a/Packages/ohif-cornerstone/client/jquery.hammer.js b/Packages/ohif-cornerstone/client/jquery.hammer.js deleted file mode 100644 index a812c7609..000000000 --- a/Packages/ohif-cornerstone/client/jquery.hammer.js +++ /dev/null @@ -1,33 +0,0 @@ -(function(factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery', 'hammerjs'], factory); - } else if (typeof exports === 'object') { - factory(require('jquery'), require('hammerjs')); - } else { - factory(jQuery, Hammer); - } -}(function($, Hammer) { - function hammerify(el, options) { - var $el = $(el); - if(!$el.data("hammer")) { - $el.data("hammer", new Hammer($el[0], options)); - } - } - - $.fn.hammer = function(options) { - return this.each(function() { - hammerify(this, options); - }); - }; - - // extend the emit method to also trigger jQuery events - Hammer.Manager.prototype.emit = (function(originalEmit) { - return function(type, data) { - originalEmit.call(this, type, data); - $(this.element).trigger({ - type: type, - gesture: data - }); - }; - })(Hammer.Manager.prototype.emit); -})); \ No newline at end of file diff --git a/Packages/ohif-cornerstone/client/lib/classes/MetadataProvider.js b/Packages/ohif-cornerstone/client/lib/classes/MetadataProvider.js index 418fdeef2..c0c0cd00e 100644 --- a/Packages/ohif-cornerstone/client/lib/classes/MetadataProvider.js +++ b/Packages/ohif-cornerstone/client/lib/classes/MetadataProvider.js @@ -1,3 +1,4 @@ +import * as cornerstoneMath from 'cornerstone-math'; import { parsingUtils } from '../parsingUtils'; const FUNCTION = 'function'; @@ -245,22 +246,17 @@ export class MetadataProvider { } return { - frameOfReferenceUID: - instance.frameOfReferenceUID, - rows: - instance.rows, - columns: - instance.columns, + frameOfReferenceUID: instance.frameOfReferenceUID, + rows: instance.rows, + columns: instance.columns, rowCosines: new cornerstoneMath.Vector3(parseFloat(imageOrientation[0]), parseFloat(imageOrientation[1]), parseFloat(imageOrientation[2])), columnCosines: new cornerstoneMath.Vector3(parseFloat(imageOrientation[3]), parseFloat(imageOrientation[4]), parseFloat(imageOrientation[5])), imagePositionPatient: new cornerstoneMath.Vector3(parseFloat(imagePosition[0]), parseFloat(imagePosition[1]), parseFloat(imagePosition[2])), - rowPixelSpacing: - rowPixelSpacing, - columnPixelSpacing: - columnPixelSpacing, + rowPixelSpacing, + columnPixelSpacing, }; } @@ -318,7 +314,7 @@ export class MetadataProvider { } /** - * Get a bound reference to the privider function. + * Get a bound reference to the provider function. */ getProvider() { let provider = this._provider; diff --git a/Packages/ohif-cornerstone/client/lib/parsingUtils.js b/Packages/ohif-cornerstone/client/lib/parsingUtils.js index d90d0af30..c09975b38 100644 --- a/Packages/ohif-cornerstone/client/lib/parsingUtils.js +++ b/Packages/ohif-cornerstone/client/lib/parsingUtils.js @@ -1,6 +1,4 @@ -/** - * Globals: dicomParser - */ +import * as dicomParser from 'dicom-parser'; /** * A small set of utilities to help parsing DICOM element values. diff --git a/Packages/ohif-cornerstone/main.js b/Packages/ohif-cornerstone/main.js index 1f598b700..6038d8596 100644 --- a/Packages/ohif-cornerstone/main.js +++ b/Packages/ohif-cornerstone/main.js @@ -2,7 +2,7 @@ * Import namespace... */ -import { OHIF, Cornerstone } from './namespace.js'; +import { OHIF } from './namespace.js'; /** * Import scripts that will populate the Cornerstone namespace as a side effect only import. This is effectively the public API... @@ -10,4 +10,20 @@ import { OHIF, Cornerstone } from './namespace.js'; import './client/'; // which is actually: import './client/index.js'; -export { OHIF, Cornerstone }; +import * as cornerstone from 'cornerstone-core'; +import * as cornerstoneTools from 'cornerstone-tools'; +import * as cornerstoneMath from 'cornerstone-math'; +import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'; +import * as dicomParser from 'dicom-parser'; + +debugger; + +export { + cornerstone, + cornerstoneTools, + cornerstoneMath, + cornerstoneWADOImageLoader, + dicomParser +}; + +export { OHIF }; diff --git a/Packages/ohif-cornerstone/namespace.js b/Packages/ohif-cornerstone/namespace.js index ed3b82ef9..ee04377f9 100644 --- a/Packages/ohif-cornerstone/namespace.js +++ b/Packages/ohif-cornerstone/namespace.js @@ -22,4 +22,4 @@ OHIF.cornerstone = Cornerstone; * Export relevant objects */ -export { OHIF, Cornerstone }; +export { OHIF }; diff --git a/Packages/ohif-cornerstone/package.js b/Packages/ohif-cornerstone/package.js index a79e75131..61fb3b11a 100644 --- a/Packages/ohif-cornerstone/package.js +++ b/Packages/ohif-cornerstone/package.js @@ -4,45 +4,31 @@ Package.describe({ version: '0.0.1' }); +Npm.depends({ + 'hammerjs': '2.0.8', + 'jquery-hammerjs': '2.0.0', + 'cornerstone-core': '0.11.0', + 'cornerstone-tools': '0.8.9', + 'cornerstone-math': '0.1.6', + 'dicom-parser': '1.7.5', + 'cornerstone-wado-image-loader': '0.14.6' +}); + Package.onUse(function(api) { - api.versionsFrom('1.4'); + api.versionsFrom('1.5'); api.use('ecmascript'); - api.use('jquery'); - api.use('ohif:core'); - api.addFiles('client/cornerstone.js', 'client', { - bare: true - }); - api.addFiles('client/cornerstoneMath.js', 'client', { - bare: true - }); - api.addFiles('client/cornerstoneTools.js', 'client', { - bare: true - }); - api.addFiles('client/cornerstoneWADOImageLoader.js', 'client', { - bare: true - }); - api.addFiles('client/dicomParser.js', 'client', { - bare: true - }); - api.addFiles('client/hammer.js', 'client', { - bare: true - }); - api.addFiles('client/jquery.hammer.js', 'client', { - bare: true - }); - api.addAssets('public/js/cornerstoneWADOImageLoaderCodecs.es5.js', 'client'); api.addAssets('public/js/cornerstoneWADOImageLoaderWebWorker.es5.js', 'client'); + api.mainModule('main.js', 'client'); + api.export('cornerstone', 'client'); api.export('cornerstoneMath', 'client'); api.export('cornerstoneTools', 'client'); api.export('cornerstoneWADOImageLoader', 'client'); api.export('dicomParser', 'client'); - - api.mainModule('main.js', 'client'); }); diff --git a/Packages/ohif-lesiontracker/both/configuration/configuration.js b/Packages/ohif-lesiontracker/both/configuration/configuration.js index 97f9c76a4..44686b5fa 100644 --- a/Packages/ohif-lesiontracker/both/configuration/configuration.js +++ b/Packages/ohif-lesiontracker/both/configuration/configuration.js @@ -5,8 +5,6 @@ import { retrieveMeasurements, storeMeasurements, retrieveTimepoints, storeTimep import { validateMeasurements } from './dataValidation'; import { FieldLesionLocation, FieldLesionLocationResponse } from 'meteor/ohif:lesiontracker/both/schema/fields'; -console.log('OHIF-LesionTracker: Defining Configuration for Measurements'); - const newMeasurementTool = { id: 'newLesions', name: 'New Lesions' diff --git a/Packages/ohif-lesiontracker/client/compatibility/bidirectionalTool.js b/Packages/ohif-lesiontracker/client/compatibility/bidirectionalTool.js index 154c4ebeb..197b0afa1 100644 --- a/Packages/ohif-lesiontracker/client/compatibility/bidirectionalTool.js +++ b/Packages/ohif-lesiontracker/client/compatibility/bidirectionalTool.js @@ -1,845 +1,852 @@ import { OHIF } from 'meteor/ohif:core'; import { Viewerbase } from 'meteor/ohif:viewerbase'; -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { +import { cornerstone, cornerstoneMath, cornerstoneTools } from 'meteor/ohif:cornerstone'; - 'use strict'; +var toolType = 'bidirectional'; - var toolType = 'bidirectional'; +const toolDefaultStates = Viewerbase.toolManager.getToolDefaultStates(); +const shadowConfig = toolDefaultStates.shadowConfig; +const textBoxConfig = toolDefaultStates.textBoxConfig; - const toolDefaultStates = Viewerbase.toolManager.getToolDefaultStates(); - const shadowConfig = toolDefaultStates.shadowConfig; - const textBoxConfig = toolDefaultStates.textBoxConfig; +var configuration = { + getMeasurementLocationCallback: getMeasurementLocationCallback, + changeMeasurementLocationCallback: changeMeasurementLocationCallback, + ...shadowConfig, + textBox: textBoxConfig +}; - var configuration = { - getMeasurementLocationCallback: getMeasurementLocationCallback, - changeMeasurementLocationCallback: changeMeasurementLocationCallback, - ...shadowConfig, - textBox: textBoxConfig - }; +// Used to cancel tool placement +var keys = { + ESC: 27 +}; - // Used to cancel tool placement - var keys = { - ESC: 27 - }; +// The distance between the mouse and the tool to make it active +var distanceThreshold = 7; - // The distance between the mouse and the tool to make it active - var distanceThreshold = 7; +// Define a callback to get your text annotation +// This could be used, e.g. to open a modal +function getMeasurementLocationCallback(measurementData, eventData, doneCallback) { + //doneCallback(prompt('Enter your lesion location:')); +} - // Define a callback to get your text annotation - // This could be used, e.g. to open a modal - function getMeasurementLocationCallback(measurementData, eventData, doneCallback) { - //doneCallback(prompt('Enter your lesion location:')); - } +function changeMeasurementLocationCallback(measurementData, eventData, doneCallback) { + //doneCallback(prompt('Change your lesion location:')); +} - function changeMeasurementLocationCallback(measurementData, eventData, doneCallback) { - //doneCallback(prompt('Change your lesion location:')); - } - - function createNewMeasurement(mouseEventData) { - // Create the measurement data for this tool with the end handle activated - const measurementData = { - isCreating: true, - visible: true, - active: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false, - drawnIndependently: true, - allowedOutsideImage: true, - index: 0 - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: true, - drawnIndependently: true, - allowedOutsideImage: true, - index: 1 - }, - textBox: { - x: mouseEventData.currentPoints.image.x - 50, - y: mouseEventData.currentPoints.image.y - 70, - active: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - }, - perpendicularStart: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false, - locked: true, // If perpendicular line is connected to long-line - drawnIndependently: true, - allowedOutsideImage: true, - index: 2 - }, - perpendicularEnd: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - highlight: true, - active: false, - drawnIndependently: true, - allowedOutsideImage: true, - index: 3 - } +function createNewMeasurement(mouseEventData) { + // Create the measurement data for this tool with the end handle activated + const measurementData = { + isCreating: true, + visible: true, + active: true, + handles: { + start: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + highlight: true, + active: false, + drawnIndependently: true, + allowedOutsideImage: true, + index: 0 }, - longestDiameter: 0, - shortestDiameter: 0, - toolType: 'bidirectional' - }; + end: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + highlight: true, + active: true, + drawnIndependently: true, + allowedOutsideImage: true, + index: 1 + }, + textBox: { + x: mouseEventData.currentPoints.image.x - 50, + y: mouseEventData.currentPoints.image.y - 70, + active: false, + movesIndependently: false, + drawnIndependently: true, + allowedOutsideImage: true, + hasBoundingBox: true + }, + perpendicularStart: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + highlight: true, + active: false, + locked: true, // If perpendicular line is connected to long-line + drawnIndependently: true, + allowedOutsideImage: true, + index: 2 + }, + perpendicularEnd: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + highlight: true, + active: false, + drawnIndependently: true, + allowedOutsideImage: true, + index: 3 + } + }, + longestDiameter: 0, + shortestDiameter: 0, + toolType: 'bidirectional' + }; - return measurementData; + return measurementData; +} + +function addNewMeasurement(mouseEventData) { + var element = mouseEventData.element; + + // LT-29 Disable Target Measurements when pixel spacing is not available + if (!mouseEventData.image.rowPixelSpacing || !mouseEventData.image.columnPixelSpacing) { + return; } - function addNewMeasurement(mouseEventData) { - var element = mouseEventData.element; - - // LT-29 Disable Target Measurements when pixel spacing is not available - if (!mouseEventData.image.rowPixelSpacing || !mouseEventData.image.columnPixelSpacing) { - return; - } - - function doneCallback() { - measurementData.active = false; - cornerstone.updateImage(element); - } - - var measurementData = createNewMeasurement(mouseEventData); - measurementData.viewport = cornerstone.getViewport(element); - - var eventData = { - mouseButtonMask: mouseEventData.which - }; - - var config = cornerstoneTools.bidirectional.getConfiguration(); - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(element, toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.bidirectional.mouseDownActivateCallback); - $(element).off('CornerstoneToolsMouseDoubleClick', doubleClickCallback); - - // Add a flag for using Esc to cancel tool placement - var cancelled = false; - function cancelCallback(e) { - // If the Esc key was pressed, set the flag to true - if (e.which === keys.ESC) { - cancelled = true; - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - // Don't propagate this keydown event so it can't interfere - // with anything outside of this tool - return false; - } - - // Bind a one-time event listener for the Esc key - $(element).one('keydown', cancelCallback); - + function doneCallback() { + measurementData.active = false; cornerstone.updateImage(element); - - cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { - if (cancelled || cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - } else { - // Set lesionMeasurementData Session - config.getMeasurementLocationCallback(measurementData, mouseEventData, doneCallback); - } - - // Unbind the Esc keydown hook - $(element).off('keydown', cancelCallback); - - // perpendicular line is not connected to long-line - measurementData.handles.perpendicularStart.locked = false; - - $(element).on('CornerstoneToolsMouseMove', eventData, mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.bidirectional.mouseDownActivateCallback); - $(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback); - cornerstone.updateImage(element); - }); } - function addNewMeasurementTouch(touchEventData) { - var element = touchEventData.element; + var measurementData = createNewMeasurement(mouseEventData); + measurementData.viewport = cornerstone.getViewport(element); - // LT-29 Disable Target Measurements when pixel spacing is not available - if (!touchEventData.image.rowPixelSpacing || !touchEventData.image.columnPixelSpacing) { - return; + var eventData = { + mouseButtonMask: mouseEventData.which + }; + + var config = cornerstoneTools.bidirectional.getConfiguration(); + + // associate this data with this imageId so we can render it and manipulate it + cornerstoneTools.addToolState(element, toolType, measurementData); + + // since we are dragging to another place to drop the end point, we can just activate + // the end point and let the moveHandle move it for us. + $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); + $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); + $(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.bidirectional.mouseDownActivateCallback); + $(element).off('CornerstoneToolsMouseDoubleClick', doubleClickCallback); + + // Add a flag for using Esc to cancel tool placement + var cancelled = false; + function cancelCallback(e) { + // If the Esc key was pressed, set the flag to true + if (e.which === keys.ESC) { + cancelled = true; + cornerstoneTools.removeToolState(element, toolType, measurementData); } - function doneCallback() { - measurementData.active = false; - cornerstone.updateImage(element); + // Don't propagate this keydown event so it can't interfere + // with anything outside of this tool + return false; + } + + // Bind a one-time event listener for the Esc key + $(element).one('keydown', cancelCallback); + + cornerstone.updateImage(element); + + cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { + if (cancelled || cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(element, toolType, measurementData); + } else { + // Set lesionMeasurementData Session + config.getMeasurementLocationCallback(measurementData, mouseEventData, doneCallback); } - var measurementData = createNewMeasurement(touchEventData); + // Unbind the Esc keydown hook + $(element).off('keydown', cancelCallback); - var config = cornerstoneTools.bidirectional.getConfiguration(); - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(element, toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.bidirectional.touchMoveHandle); - $(element).off('CornerstoneToolsTap', cornerstoneTools.bidirectional.tapCallback); - $(element).off('CornerstoneToolsDragStartActive', cornerstoneTools.bidirectional.touchDownActivateCallback); + // perpendicular line is not connected to long-line + measurementData.handles.perpendicularStart.locked = false; + $(element).on('CornerstoneToolsMouseMove', eventData, mouseMoveCallback); + $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); + $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.bidirectional.mouseDownActivateCallback); + $(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback); cornerstone.updateImage(element); - cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() { - if (cancelled || cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - } else { - // Set lesionMeasurementData Session - config.getMeasurementLocationCallback(measurementData, touchEventData, doneCallback); - } + }); +} - // perpendicular line is not connected to long-line - measurementData.handles.perpendicularStart.locked = false; +function addNewMeasurementTouch(touchEventData) { + var element = touchEventData.element; - $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.bidirectional.touchMoveHandle); - $(element).on('CornerstoneToolsTap', cornerstoneTools.bidirectional.tapCallback); - $(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.bidirectional.touchDownActivateCallback); - cornerstone.updateImage(element); - }); + // LT-29 Disable Target Measurements when pixel spacing is not available + if (!touchEventData.image.rowPixelSpacing || !touchEventData.image.columnPixelSpacing) { + return; } - function pointNearTool(element, data, coords) { - var lineSegment = { - start: cornerstone.pixelToCanvas(element, data.handles.start), - end: cornerstone.pixelToCanvas(element, data.handles.end) - }; - var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); + function doneCallback() { + measurementData.active = false; + cornerstone.updateImage(element); + } + + var measurementData = createNewMeasurement(touchEventData); + + var config = cornerstoneTools.bidirectional.getConfiguration(); + + // associate this data with this imageId so we can render it and manipulate it + cornerstoneTools.addToolState(element, toolType, measurementData); + + // since we are dragging to another place to drop the end point, we can just activate + // the end point and let the moveHandle move it for us. + $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.bidirectional.touchMoveHandle); + $(element).off('CornerstoneToolsTap', cornerstoneTools.bidirectional.tapCallback); + $(element).off('CornerstoneToolsDragStartActive', cornerstoneTools.bidirectional.touchDownActivateCallback); + + cornerstone.updateImage(element); + cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() { + if (cancelled || cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(element, toolType, measurementData); + } else { + // Set lesionMeasurementData Session + config.getMeasurementLocationCallback(measurementData, touchEventData, doneCallback); + } + + // perpendicular line is not connected to long-line + measurementData.handles.perpendicularStart.locked = false; + + $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.bidirectional.touchMoveHandle); + $(element).on('CornerstoneToolsTap', cornerstoneTools.bidirectional.tapCallback); + $(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.bidirectional.touchDownActivateCallback); + cornerstone.updateImage(element); + }); +} + +function pointNearTool(element, data, coords) { + var lineSegment = { + start: cornerstone.pixelToCanvas(element, data.handles.start), + end: cornerstone.pixelToCanvas(element, data.handles.end) + }; + var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); + + if (cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { + return true; + } + + if (pointNearPerpendicular(element, data.handles, coords)) { + return true; + } + + return (distanceToPoint < distanceThreshold); +} + +function pointNearPerpendicular(element, handles, coords) { + var lineSegment = { + start: cornerstone.pixelToCanvas(element, handles.perpendicularStart), + end: cornerstone.pixelToCanvas(element, handles.perpendicularEnd) + }; + var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); + return (distanceToPoint < distanceThreshold); +} + +// Move long-axis start point +function perpendicularBothFixedLeft(eventData, data) { + var longLine = { + start: { + x: data.handles.start.x, + y: data.handles.start.y + }, + end: { + x: data.handles.end.x, + y: data.handles.end.y + } + }; + + var perpendicularLine = { + start: { + x: data.handles.perpendicularStart.x, + y: data.handles.perpendicularStart.y + }, + end: { + x: data.handles.perpendicularEnd.x, + y: data.handles.perpendicularEnd.y + } + }; + + var intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + + var distanceFromPerpendicularP1 = cornerstoneMath.point.distance(data.handles.perpendicularStart, intersection); + var distanceFromPerpendicularP2 = cornerstoneMath.point.distance(data.handles.perpendicularEnd, intersection); + + var distanceToLineP2 = cornerstoneMath.point.distance(data.handles.end, intersection); + var newLineLength = cornerstoneMath.point.distance(data.handles.end, eventData.currentPoints.image); + + if (newLineLength <= distanceToLineP2) { + return false; + } + + var dx = (data.handles.end.x - eventData.currentPoints.image.x) / newLineLength; + var dy = (data.handles.end.y - eventData.currentPoints.image.y) / newLineLength; + + var k = distanceToLineP2 / newLineLength; + + var newIntersection = { + x: data.handles.end.x + ((eventData.currentPoints.image.x - data.handles.end.x) * k), + y: data.handles.end.y + ((eventData.currentPoints.image.y - data.handles.end.y) * k) + }; + + data.handles.perpendicularStart.x = newIntersection.x - distanceFromPerpendicularP1 * dy; + data.handles.perpendicularStart.y = newIntersection.y + distanceFromPerpendicularP1 * dx; + + data.handles.perpendicularEnd.x = newIntersection.x + distanceFromPerpendicularP2 * dy; + data.handles.perpendicularEnd.y = newIntersection.y - distanceFromPerpendicularP2 * dx; + + return true; + +} + +// Move long-axis end point +function perpendicularBothFixedRight(eventData, data) { + var longLine = { + start: { + x: data.handles.start.x, + y: data.handles.start.y + }, + end: { + x: data.handles.end.x, + y: data.handles.end.y + } + }; + + var perpendicularLine = { + start: { + x: data.handles.perpendicularStart.x, + y: data.handles.perpendicularStart.y + }, + end: { + x: data.handles.perpendicularEnd.x, + y: data.handles.perpendicularEnd.y + } + }; + + var intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + + var distanceFromPerpendicularP1 = cornerstoneMath.point.distance(data.handles.perpendicularStart, intersection); + var distanceFromPerpendicularP2 = cornerstoneMath.point.distance(data.handles.perpendicularEnd, intersection); + + var distanceToLineP2 = cornerstoneMath.point.distance(data.handles.start, intersection); + var newLineLength = cornerstoneMath.point.distance(data.handles.start, eventData.currentPoints.image); + + if (newLineLength <= distanceToLineP2) { + return false; + } + + var dx = (data.handles.start.x - eventData.currentPoints.image.x) / newLineLength; + var dy = (data.handles.start.y - eventData.currentPoints.image.y) / newLineLength; + + var k = distanceToLineP2 / newLineLength; + + var newIntersection = { + x: data.handles.start.x + ((eventData.currentPoints.image.x - data.handles.start.x) * k), + y: data.handles.start.y + ((eventData.currentPoints.image.y - data.handles.start.y) * k) + }; + + data.handles.perpendicularStart.x = newIntersection.x + distanceFromPerpendicularP1 * dy; + data.handles.perpendicularStart.y = newIntersection.y - distanceFromPerpendicularP1 * dx; + + data.handles.perpendicularEnd.x = newIntersection.x - distanceFromPerpendicularP2 * dy; + data.handles.perpendicularEnd.y = newIntersection.y + distanceFromPerpendicularP2 * dx; + + return true; + +} + +// Move perpendicular line start point +function perpendicularLeftFixedPoint(eventData, data) { + var fudgeFactor = 1; + + var fixedPoint = data.handles.perpendicularEnd; + var movedPoint = eventData.currentPoints.image; + + var distanceFromFixed = cornerstoneMath.lineSegment.distanceToPoint(data.handles, fixedPoint); + var distanceFromMoved = cornerstoneMath.lineSegment.distanceToPoint(data.handles, movedPoint); + + var distanceBetweenPoints = cornerstoneMath.point.distance(fixedPoint, movedPoint); + + var total = distanceFromFixed + distanceFromMoved; + + if (distanceBetweenPoints <= distanceFromFixed) { + return false; + } + + var length = cornerstoneMath.point.distance(data.handles.start, data.handles.end); + if (length === 0) { + return false; + } + + var dx = (data.handles.start.x - data.handles.end.x) / length; + var dy = (data.handles.start.y - data.handles.end.y) / length; + + var adjustedLineP1 = { + x: data.handles.start.x - fudgeFactor * dx, + y: data.handles.start.y - fudgeFactor * dy + }; + var adjustedLineP2 = { + x: data.handles.end.x + fudgeFactor * dx, + y: data.handles.end.y + fudgeFactor * dy + }; + + data.handles.perpendicularStart.x = movedPoint.x; + data.handles.perpendicularStart.y = movedPoint.y; + data.handles.perpendicularEnd.x = movedPoint.x - total * dy; + data.handles.perpendicularEnd.y = movedPoint.y + total * dx; + + var longLine = { + start: { + x: data.handles.start.x, + y: data.handles.start.y + }, + end: { + x: data.handles.end.x, + y: data.handles.end.y + } + }; + + var perpendicularLine = { + start: { + x: data.handles.perpendicularStart.x, + y: data.handles.perpendicularStart.y + }, + end: { + x: data.handles.perpendicularEnd.x, + y: data.handles.perpendicularEnd.y + } + }; + + var intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + if (!intersection) { + if (cornerstoneMath.point.distance(movedPoint, data.handles.start) > cornerstoneMath.point.distance(movedPoint, data.handles.end)) { + data.handles.perpendicularStart.x = adjustedLineP2.x + distanceFromMoved * dy; + data.handles.perpendicularStart.y = adjustedLineP2.y - distanceFromMoved * dx; + data.handles.perpendicularEnd.x = data.handles.perpendicularStart.x - total * dy; + data.handles.perpendicularEnd.y = data.handles.perpendicularStart.y + total * dx; + } else { + data.handles.perpendicularStart.x = adjustedLineP1.x + distanceFromMoved * dy; + data.handles.perpendicularStart.y = adjustedLineP1.y - distanceFromMoved * dx; + data.handles.perpendicularEnd.x = data.handles.perpendicularStart.x - total * dy; + data.handles.perpendicularEnd.y = data.handles.perpendicularStart.y + total * dx; + } + } + + return true; +} + +// Move perpendicular line end point +function perpendicularRightFixedPoint(eventData, data) { + var fudgeFactor = 1; + + var fixedPoint = data.handles.perpendicularStart; + var movedPoint = eventData.currentPoints.image; + + var distanceFromFixed = cornerstoneMath.lineSegment.distanceToPoint(data.handles, fixedPoint); + var distanceFromMoved = cornerstoneMath.lineSegment.distanceToPoint(data.handles, movedPoint); + + var distanceBetweenPoints = cornerstoneMath.point.distance(fixedPoint, movedPoint); + + var total = distanceFromFixed + distanceFromMoved; + + if (distanceBetweenPoints <= distanceFromFixed) { + return false; + } + + var length = cornerstoneMath.point.distance(data.handles.start, data.handles.end); + var dx = (data.handles.start.x - data.handles.end.x) / length; + var dy = (data.handles.start.y - data.handles.end.y) / length; + + var adjustedLineP1 = { + x: data.handles.start.x - fudgeFactor * dx, + y: data.handles.start.y - fudgeFactor * dy + }; + var adjustedLineP2 = { + x: data.handles.end.x + fudgeFactor * dx, + y: data.handles.end.y + fudgeFactor * dy + }; + + data.handles.perpendicularStart.x = movedPoint.x + total * dy; + data.handles.perpendicularStart.y = movedPoint.y - total * dx; + data.handles.perpendicularEnd.x = movedPoint.x; + data.handles.perpendicularEnd.y = movedPoint.y; + data.handles.perpendicularEnd.locked = false; + data.handles.perpendicularStart.locked = false; + + var longLine = { + start: { + x: data.handles.start.x, + y: data.handles.start.y + }, + end: { + x: data.handles.end.x, + y: data.handles.end.y + } + }; + + var perpendicularLine = { + start: { + x: data.handles.perpendicularStart.x, + y: data.handles.perpendicularStart.y + }, + end: { + x: data.handles.perpendicularEnd.x, + y: data.handles.perpendicularEnd.y + } + }; + + var intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + if (!intersection) { + if (cornerstoneMath.point.distance(movedPoint, data.handles.start) > cornerstoneMath.point.distance(movedPoint, data.handles.end)) { + data.handles.perpendicularEnd.x = adjustedLineP2.x - distanceFromMoved * dy; + data.handles.perpendicularEnd.y = adjustedLineP2.y + distanceFromMoved * dx; + data.handles.perpendicularStart.x = data.handles.perpendicularEnd.x + total * dy; + data.handles.perpendicularStart.y = data.handles.perpendicularEnd.y - total * dx; + + return true; + + } else { + data.handles.perpendicularEnd.x = adjustedLineP1.x - distanceFromMoved * dy; + data.handles.perpendicularEnd.y = adjustedLineP1.y + distanceFromMoved * dx; + data.handles.perpendicularStart.x = data.handles.perpendicularEnd.x + total * dy; + data.handles.perpendicularStart.y = data.handles.perpendicularEnd.y - total * dx; - if (cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { return true; } - if (pointNearPerpendicular(element, data.handles, coords)) { - return true; - } - - return (distanceToPoint < distanceThreshold); } - function pointNearPerpendicular(element, handles, coords) { - var lineSegment = { - start: cornerstone.pixelToCanvas(element, handles.perpendicularStart), - end: cornerstone.pixelToCanvas(element, handles.perpendicularEnd) - }; - var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - return (distanceToPoint < distanceThreshold); - } + return true; +} - // Move long-axis start point - function perpendicularBothFixedLeft(eventData, data) { - var longLine = { - start: { - x: data.handles.start.x, - y: data.handles.start.y - }, - end: { - x: data.handles.end.x, - y: data.handles.end.y - } - }; +// Sets position of handles(start, end, perpendicularStart, perpendicularEnd) +function setHandlesPosition(handle, eventData, data) { - var perpendicularLine = { - start: { - x: data.handles.perpendicularStart.x, - y: data.handles.perpendicularStart.y - }, - end: { - x: data.handles.perpendicularEnd.x, - y: data.handles.perpendicularEnd.y - } - }; + let movedPoint, + outOfBounds, + result, + intersection, + d1, + d2; - var intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + let longLine = {}, + perpendicularLine = {}; - var distanceFromPerpendicularP1 = cornerstoneMath.point.distance(data.handles.perpendicularStart, intersection); - var distanceFromPerpendicularP2 = cornerstoneMath.point.distance(data.handles.perpendicularEnd, intersection); - - var distanceToLineP2 = cornerstoneMath.point.distance(data.handles.end, intersection); - var newLineLength = cornerstoneMath.point.distance(data.handles.end, eventData.currentPoints.image); - - if (newLineLength <= distanceToLineP2) { - return false; + if (handle.index === 0) { + // if long-axis start point is moved + result = perpendicularBothFixedLeft(eventData, data); + if (result) { + handle.x = eventData.currentPoints.image.x; + handle.y = eventData.currentPoints.image.y; + } else { + eventData.currentPoints.image.x = handle.x; + eventData.currentPoints.image.y = handle.y; } - var dx = (data.handles.end.x - eventData.currentPoints.image.x) / newLineLength; - var dy = (data.handles.end.y - eventData.currentPoints.image.y) / newLineLength; - - var k = distanceToLineP2 / newLineLength; - - var newIntersection = { - x: data.handles.end.x + ((eventData.currentPoints.image.x - data.handles.end.x) * k), - y: data.handles.end.y + ((eventData.currentPoints.image.y - data.handles.end.y) * k) - }; - - data.handles.perpendicularStart.x = newIntersection.x - distanceFromPerpendicularP1 * dy; - data.handles.perpendicularStart.y = newIntersection.y + distanceFromPerpendicularP1 * dx; - - data.handles.perpendicularEnd.x = newIntersection.x + distanceFromPerpendicularP2 * dy; - data.handles.perpendicularEnd.y = newIntersection.y - distanceFromPerpendicularP2 * dx; - - return true; - - } - - // Move long-axis end point - function perpendicularBothFixedRight(eventData, data) { - var longLine = { - start: { - x: data.handles.start.x, - y: data.handles.start.y - }, - end: { - x: data.handles.end.x, - y: data.handles.end.y - } - }; - - var perpendicularLine = { - start: { - x: data.handles.perpendicularStart.x, - y: data.handles.perpendicularStart.y - }, - end: { - x: data.handles.perpendicularEnd.x, - y: data.handles.perpendicularEnd.y - } - }; - - var intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); - - var distanceFromPerpendicularP1 = cornerstoneMath.point.distance(data.handles.perpendicularStart, intersection); - var distanceFromPerpendicularP2 = cornerstoneMath.point.distance(data.handles.perpendicularEnd, intersection); - - var distanceToLineP2 = cornerstoneMath.point.distance(data.handles.start, intersection); - var newLineLength = cornerstoneMath.point.distance(data.handles.start, eventData.currentPoints.image); - - if (newLineLength <= distanceToLineP2) { - return false; + } else if (handle.index === 1) { + // if long-axis end point is moved + result = perpendicularBothFixedRight(eventData, data); + if (result) { + handle.x = eventData.currentPoints.image.x; + handle.y = eventData.currentPoints.image.y; + } else { + eventData.currentPoints.image.x = handle.x; + eventData.currentPoints.image.y = handle.y; } - var dx = (data.handles.start.x - eventData.currentPoints.image.x) / newLineLength; - var dy = (data.handles.start.y - eventData.currentPoints.image.y) / newLineLength; - - var k = distanceToLineP2 / newLineLength; - - var newIntersection = { - x: data.handles.start.x + ((eventData.currentPoints.image.x - data.handles.start.x) * k), - y: data.handles.start.y + ((eventData.currentPoints.image.y - data.handles.start.y) * k) + } else if (handle.index === 2) { + outOfBounds = false; + // if perpendicular start point is moved + longLine.start = { + x: data.handles.start.x, + y: data.handles.start.y + }; + longLine.end = { + x: data.handles.end.x, + y: data.handles.end.y }; - data.handles.perpendicularStart.x = newIntersection.x + distanceFromPerpendicularP1 * dy; - data.handles.perpendicularStart.y = newIntersection.y - distanceFromPerpendicularP1 * dx; - - data.handles.perpendicularEnd.x = newIntersection.x - distanceFromPerpendicularP2 * dy; - data.handles.perpendicularEnd.y = newIntersection.y + distanceFromPerpendicularP2 * dx; - - return true; - - } - - // Move perpendicular line start point - function perpendicularLeftFixedPoint(eventData, data) { - var fudgeFactor = 1; - - var fixedPoint = data.handles.perpendicularEnd; - var movedPoint = eventData.currentPoints.image; - - var distanceFromFixed = cornerstoneMath.lineSegment.distanceToPoint(data.handles, fixedPoint); - var distanceFromMoved = cornerstoneMath.lineSegment.distanceToPoint(data.handles, movedPoint); - - var distanceBetweenPoints = cornerstoneMath.point.distance(fixedPoint, movedPoint); - - var total = distanceFromFixed + distanceFromMoved; - - if (distanceBetweenPoints <= distanceFromFixed) { - return false; - } - - var length = cornerstoneMath.point.distance(data.handles.start, data.handles.end); - var dx = (data.handles.start.x - data.handles.end.x) / length; - var dy = (data.handles.start.y - data.handles.end.y) / length; - - var adjustedLineP1 = { - x: data.handles.start.x - fudgeFactor * dx, - y: data.handles.start.y - fudgeFactor * dy + perpendicularLine.start = { + x: data.handles.perpendicularEnd.x, + y: data.handles.perpendicularEnd.y }; - var adjustedLineP2 = { - x: data.handles.end.x + fudgeFactor * dx, - y: data.handles.end.y + fudgeFactor * dy + perpendicularLine.end = { + x: eventData.currentPoints.image.x, + y: eventData.currentPoints.image.y }; - data.handles.perpendicularStart.x = movedPoint.x; - data.handles.perpendicularStart.y = movedPoint.y; - data.handles.perpendicularEnd.x = movedPoint.x - total * dy; - data.handles.perpendicularEnd.y = movedPoint.y + total * dx; - - var longLine = { - start: { - x: data.handles.start.x, - y: data.handles.start.y - }, - end: { - x: data.handles.end.x, - y: data.handles.end.y - } - }; - - var perpendicularLine = { - start: { - x: data.handles.perpendicularStart.x, - y: data.handles.perpendicularStart.y - }, - end: { - x: data.handles.perpendicularEnd.x, - y: data.handles.perpendicularEnd.y - } - }; - - var intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); if (!intersection) { - if (cornerstoneMath.point.distance(movedPoint, data.handles.start) > cornerstoneMath.point.distance(movedPoint, data.handles.end)) { - data.handles.perpendicularStart.x = adjustedLineP2.x + distanceFromMoved * dy; - data.handles.perpendicularStart.y = adjustedLineP2.y - distanceFromMoved * dx; - data.handles.perpendicularEnd.x = data.handles.perpendicularStart.x - total * dy; - data.handles.perpendicularEnd.y = data.handles.perpendicularStart.y + total * dx; - - return true; - - } else { - data.handles.perpendicularStart.x = adjustedLineP1.x + distanceFromMoved * dy; - data.handles.perpendicularStart.y = adjustedLineP1.y - distanceFromMoved * dx; - data.handles.perpendicularEnd.x = data.handles.perpendicularStart.x - total * dy; - data.handles.perpendicularEnd.y = data.handles.perpendicularStart.y + total * dx; - - return true; - } - - } - - return true; - } - - // Move perpendicular line end point - function perpendicularRightFixedPoint(eventData, data) { - var fudgeFactor = 1; - - var fixedPoint = data.handles.perpendicularStart; - var movedPoint = eventData.currentPoints.image; - - var distanceFromFixed = cornerstoneMath.lineSegment.distanceToPoint(data.handles, fixedPoint); - var distanceFromMoved = cornerstoneMath.lineSegment.distanceToPoint(data.handles, movedPoint); - - var distanceBetweenPoints = cornerstoneMath.point.distance(fixedPoint, movedPoint); - - var total = distanceFromFixed + distanceFromMoved; - - if (distanceBetweenPoints <= distanceFromFixed) { - return false; - } - - var length = cornerstoneMath.point.distance(data.handles.start, data.handles.end); - var dx = (data.handles.start.x - data.handles.end.x) / length; - var dy = (data.handles.start.y - data.handles.end.y) / length; - - var adjustedLineP1 = { - x: data.handles.start.x - fudgeFactor * dx, - y: data.handles.start.y - fudgeFactor * dy - }; - var adjustedLineP2 = { - x: data.handles.end.x + fudgeFactor * dx, - y: data.handles.end.y + fudgeFactor * dy - }; - - data.handles.perpendicularStart.x = movedPoint.x + total * dy; - data.handles.perpendicularStart.y = movedPoint.y - total * dx; - data.handles.perpendicularEnd.x = movedPoint.x; - data.handles.perpendicularEnd.y = movedPoint.y; - data.handles.perpendicularEnd.locked = false; - data.handles.perpendicularStart.locked = false; - - var longLine = { - start: { - x: data.handles.start.x, - y: data.handles.start.y - }, - end: { - x: data.handles.end.x, - y: data.handles.end.y - } - }; - - var perpendicularLine = { - start: { - x: data.handles.perpendicularStart.x, - y: data.handles.perpendicularStart.y - }, - end: { - x: data.handles.perpendicularEnd.x, - y: data.handles.perpendicularEnd.y - } - }; - - var intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); - if (!intersection) { - if (cornerstoneMath.point.distance(movedPoint, data.handles.start) > cornerstoneMath.point.distance(movedPoint, data.handles.end)) { - data.handles.perpendicularEnd.x = adjustedLineP2.x - distanceFromMoved * dy; - data.handles.perpendicularEnd.y = adjustedLineP2.y + distanceFromMoved * dx; - data.handles.perpendicularStart.x = data.handles.perpendicularEnd.x + total * dy; - data.handles.perpendicularStart.y = data.handles.perpendicularEnd.y - total * dx; - - return true; - - } else { - data.handles.perpendicularEnd.x = adjustedLineP1.x - distanceFromMoved * dy; - data.handles.perpendicularEnd.y = adjustedLineP1.y + distanceFromMoved * dx; - data.handles.perpendicularStart.x = data.handles.perpendicularEnd.x + total * dy; - data.handles.perpendicularStart.y = data.handles.perpendicularEnd.y - total * dx; - - return true; - } - - } - - return true; - } - - // Sets position of handles(start, end, perpendicularStart, perpendicularEnd) - function setHandlesPosition(handle, eventData, data) { - - let movedPoint, - outOfBounds, - result, - intersection, - d1, - d2; - - let longLine = {}, - perpendicularLine = {}; - - if (handle.index === 0) { - // if long-axis start point is moved - result = perpendicularBothFixedLeft(eventData, data); - if (result) { - handle.x = eventData.currentPoints.image.x; - handle.y = eventData.currentPoints.image.y; - } else { - eventData.currentPoints.image.x = handle.x; - eventData.currentPoints.image.y = handle.y; - } - - } else if (handle.index === 1) { - // if long-axis end point is moved - result = perpendicularBothFixedRight(eventData, data); - if (result) { - handle.x = eventData.currentPoints.image.x; - handle.y = eventData.currentPoints.image.y; - } else { - eventData.currentPoints.image.x = handle.x; - eventData.currentPoints.image.y = handle.y; - } - - } else if (handle.index === 2) { - outOfBounds = false; - // if perpendicular start point is moved - longLine.start = { - x: data.handles.start.x, - y: data.handles.start.y - }; - longLine.end = { - x: data.handles.end.x, - y: data.handles.end.y - }; - - perpendicularLine.start = { - x: data.handles.perpendicularEnd.x, - y: data.handles.perpendicularEnd.y - }; perpendicularLine.end = { - x: eventData.currentPoints.image.x, - y: eventData.currentPoints.image.y + x: data.handles.perpendicularStart.x, + y: data.handles.perpendicularStart.y }; intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); - if (!intersection) { - perpendicularLine.end = { - x: data.handles.perpendicularStart.x, - y: data.handles.perpendicularStart.y - }; - intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + d1 = cornerstoneMath.point.distance(intersection, data.handles.start); + d2 = cornerstoneMath.point.distance(intersection, data.handles.end); - d1 = cornerstoneMath.point.distance(intersection, data.handles.start); - d2 = cornerstoneMath.point.distance(intersection, data.handles.end); - - if (!intersection || d1 < 3 || d2 < 3) { - outOfBounds = true; - } + if (!intersection || d1 < 3 || d2 < 3) { + outOfBounds = true; } + } - movedPoint = false; + movedPoint = false; - if (!outOfBounds) { - movedPoint = perpendicularLeftFixedPoint(eventData, data); + if (!outOfBounds) { + movedPoint = perpendicularLeftFixedPoint(eventData, data); - if (!movedPoint) { - eventData.currentPoints.image.x = data.handles.perpendicularStart.x; - eventData.currentPoints.image.y = data.handles.perpendicularStart.y; - } + if (!movedPoint) { + eventData.currentPoints.image.x = data.handles.perpendicularStart.x; + eventData.currentPoints.image.y = data.handles.perpendicularStart.y; } + } - } else if (handle.index === 3) { - outOfBounds = false; + } else if (handle.index === 3) { + outOfBounds = false; - // if perpendicular end point is moved - longLine.start = { - x: data.handles.start.x, - y: data.handles.start.y - }; - longLine.end = { - x: data.handles.end.x, - y: data.handles.end.y - }; + // if perpendicular end point is moved + longLine.start = { + x: data.handles.start.x, + y: data.handles.start.y + }; + longLine.end = { + x: data.handles.end.x, + y: data.handles.end.y + }; - perpendicularLine.start = { - x: data.handles.perpendicularStart.x, - y: data.handles.perpendicularStart.y - }; + perpendicularLine.start = { + x: data.handles.perpendicularStart.x, + y: data.handles.perpendicularStart.y + }; + perpendicularLine.end = { + x: eventData.currentPoints.image.x, + y: eventData.currentPoints.image.y + }; + + intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + if (!intersection) { perpendicularLine.end = { - x: eventData.currentPoints.image.x, - y: eventData.currentPoints.image.y + x: data.handles.perpendicularEnd.x, + y: data.handles.perpendicularEnd.y }; intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); - if (!intersection) { - perpendicularLine.end = { - x: data.handles.perpendicularEnd.x, - y: data.handles.perpendicularEnd.y - }; - intersection = cornerstoneMath.lineSegment.intersectLine(longLine, perpendicularLine); + d1 = cornerstoneMath.point.distance(intersection, data.handles.start); + d2 = cornerstoneMath.point.distance(intersection, data.handles.end); - d1 = cornerstoneMath.point.distance(intersection, data.handles.start); - d2 = cornerstoneMath.point.distance(intersection, data.handles.end); - - if (!intersection || d1 < 3 || d2 < 3) { - outOfBounds = true; - } + if (!intersection || d1 < 3 || d2 < 3) { + outOfBounds = true; } + } - movedPoint = false; + movedPoint = false; - if (!outOfBounds) { - movedPoint = perpendicularRightFixedPoint(eventData, data); + if (!outOfBounds) { + movedPoint = perpendicularRightFixedPoint(eventData, data); - if (!movedPoint) { - eventData.currentPoints.image.x = data.handles.perpendicularEnd.x; - eventData.currentPoints.image.y = data.handles.perpendicularEnd.y; - } + if (!movedPoint) { + eventData.currentPoints.image.x = data.handles.perpendicularEnd.x; + eventData.currentPoints.image.y = data.handles.perpendicularEnd.y; } } } +} - // Sets drawnIndependently property of control points(handles) - function setControlPoints(handles, value) { - Object.keys(handles).forEach(function(name) { - if (name !== 'textBox') { - var handle = handles[name]; - handle.drawnIndependently = value; - } - }); - } +// Sets drawnIndependently property of control points(handles) +function setControlPoints(handles, value) { + Object.keys(handles).forEach(function(name) { + if (name !== 'textBox') { + var handle = handles[name]; + handle.drawnIndependently = value; + } + }); +} - //****************************************/ - // Cornerstone Methods - //****************************************/ +//****************************************/ +// Cornerstone Methods +//****************************************/ - function moveHandle(mouseEventData, toolType, data, handle, doneMovingCallback, preventHandleOutsideImage) { - var element = mouseEventData.element; - var distanceFromTool = { - x: handle.x - mouseEventData.currentPoints.image.x, - y: handle.y - mouseEventData.currentPoints.image.y +function moveHandle(mouseEventData, toolType, data, handle, doneMovingCallback, preventHandleOutsideImage) { + var element = mouseEventData.element; + var distanceFromTool = { + x: handle.x - mouseEventData.currentPoints.image.x, + y: handle.y - mouseEventData.currentPoints.image.y + }; + + function mouseDragCallback(e, eventData) { + handle.active = true; + + if (handle.index === undefined || handle.index === null) { + handle.x = eventData.currentPoints.image.x + distanceFromTool.x; + handle.y = eventData.currentPoints.image.y + distanceFromTool.y; + } else { + setHandlesPosition(handle, eventData, data); + } + + if (preventHandleOutsideImage) { + handle.x = Math.max(handle.x, 0); + handle.x = Math.min(handle.x, eventData.image.width); + + handle.y = Math.max(handle.y, 0); + handle.y = Math.min(handle.y, eventData.image.height); + } + + cornerstone.updateImage(element); + + var eventType = 'CornerstoneToolsMeasurementModified'; + var modifiedEventData = { + toolType: toolType, + element: element, + measurementData: data }; + $(element).trigger(eventType, modifiedEventData); + } - function mouseDragCallback(e, eventData) { - handle.active = true; + $(element).on('CornerstoneToolsMouseDrag', mouseDragCallback); - if (handle.index === undefined || handle.index === null) { - handle.x = eventData.currentPoints.image.x + distanceFromTool.x; - handle.y = eventData.currentPoints.image.y + distanceFromTool.y; - } else { - setHandlesPosition(handle, eventData, data); + function mouseUpCallback() { + handle.active = false; + $(element).off('CornerstoneToolsMouseDrag', mouseDragCallback); + $(element).off('CornerstoneToolsMouseUp', mouseUpCallback); + $(element).off('CornerstoneToolsMouseClick', mouseUpCallback); + cornerstone.updateImage(element); + + if (typeof doneMovingCallback === 'function') { + doneMovingCallback(); + } + } + + $(element).on('CornerstoneToolsMouseUp', mouseUpCallback); + $(element).on('CornerstoneToolsMouseClick', mouseUpCallback); +} + +// mouseMoveCallback is used to hide handles when mouse is away +function mouseMoveCallback(e, eventData) { + cornerstoneTools.toolCoordinates.setCoords(eventData); + // if a mouse button is down, do nothing + if (eventData.which !== 0) { + return; + } + + // if we have no tool data for this element, do nothing + var toolData = cornerstoneTools.getToolState(eventData.element, toolType); + if (!toolData) { + return; + } + + // We have tool data, search through all data + // and see if we can activate a handle + var imageNeedsUpdate = false; + for (var i = 0; i < toolData.data.length; i++) { + // get the cursor position in canvas coordinates + var coords = eventData.currentPoints.canvas; + + var data = toolData.data[i]; + if (cornerstoneTools.handleActivator(eventData.element, data.handles, coords) === true) { + imageNeedsUpdate = true; + } + + if ((pointNearTool(eventData.element, data, coords) && !data.active) || (!pointNearTool(eventData.element, data, coords) && data.active)) { + data.active = !data.active; + + // Set handles visibility + setControlPoints(data.handles, !data.active); + imageNeedsUpdate = true; + } + } + + // Handle activation status changed, redraw the image + if (imageNeedsUpdate === true) { + cornerstone.updateImage(eventData.element); + } +} + +// mouseDowCallback is used to restrict behaviour of perpendicular-line +function mouseDownCallback(e, eventData) { + var data; + var element = eventData.element; + + function handleDoneMove() { + data.active = false; + data.invalidated = true; + if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(element, toolType, data); + } + + cornerstone.updateImage(element); + $(element).on('CornerstoneToolsMouseMove', eventData, mouseMoveCallback); + } + + if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { + var coords = eventData.startPoints.canvas; + var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); + + var i; + + // now check to see if there is a handle we can move + if (toolData) { + for (i = 0; i < toolData.data.length; i++) { + data = toolData.data[i]; + // Hide control points + setControlPoints(data.handles, true); + var distance = 5; + var handle = cornerstoneTools.getHandleNearImagePoint(element, data.handles, coords, distance); + if (handle) { + $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); + data.active = true; + + moveHandle(eventData, toolType, data, handle, handleDoneMove); + e.stopImmediatePropagation(); + return false; + } } + } - if (preventHandleOutsideImage) { - handle.x = Math.max(handle.x, 0); - handle.x = Math.min(handle.x, eventData.image.width); - - handle.y = Math.max(handle.y, 0); - handle.y = Math.min(handle.y, eventData.image.height); - } - - cornerstone.updateImage(element); - - var eventType = 'CornerstoneToolsMeasurementModified'; - var modifiedEventData = { - toolType: toolType, - element: element, - measurementData: data + // Now check to see if there is a line we can move + // now check to see if we have a tool that we can move + if (toolData && pointNearTool) { + var options = { + deleteIfHandleOutsideImage: true, + preventHandleOutsideImage: false }; - $(element).trigger(eventType, modifiedEventData); - } - $(element).on('CornerstoneToolsMouseDrag', mouseDragCallback); - - function mouseUpCallback() { - handle.active = false; - $(element).off('CornerstoneToolsMouseDrag', mouseDragCallback); - $(element).off('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).off('CornerstoneToolsMouseClick', mouseUpCallback); - cornerstone.updateImage(element); - - if (typeof doneMovingCallback === 'function') { - doneMovingCallback(); - } - } - - $(element).on('CornerstoneToolsMouseUp', mouseUpCallback); - $(element).on('CornerstoneToolsMouseClick', mouseUpCallback); - } - - // mouseMoveCallback is used to hide handles when mouse is away - function mouseMoveCallback(e, eventData) { - cornerstoneTools.toolCoordinates.setCoords(eventData); - // if a mouse button is down, do nothing - if (eventData.which !== 0) { - return; - } - - // if we have no tool data for this element, do nothing - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData) { - return; - } - - // We have tool data, search through all data - // and see if we can activate a handle - var imageNeedsUpdate = false; - for (var i = 0; i < toolData.data.length; i++) { - // get the cursor position in canvas coordinates - var coords = eventData.currentPoints.canvas; - - var data = toolData.data[i]; - if (cornerstoneTools.handleActivator(eventData.element, data.handles, coords) === true) { - imageNeedsUpdate = true; - } - - if ((pointNearTool(eventData.element, data, coords) && !data.active) || (!pointNearTool(eventData.element, data, coords) && data.active)) { - data.active = !data.active; - - // Set handles visibility - setControlPoints(data.handles, !data.active); - imageNeedsUpdate = true; - } - } - - // Handle activation status changed, redraw the image - if (imageNeedsUpdate === true) { - cornerstone.updateImage(eventData.element); - } - } - - // mouseDowCallback is used to restrict behaviour of perpendicular-line - function mouseDownCallback(e, eventData) { - var data; - var element = eventData.element; - - function handleDoneMove() { - data.active = false; - data.invalidated = true; - if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, data); - } - - cornerstone.updateImage(element); - $(element).on('CornerstoneToolsMouseMove', eventData, mouseMoveCallback); - } - - if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - var coords = eventData.startPoints.canvas; - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - - var i; - - // now check to see if there is a handle we can move - if (toolData) { - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - // Hide control points - setControlPoints(data.handles, true); - var distance = 5; - var handle = cornerstoneTools.getHandleNearImagePoint(element, data.handles, coords, distance); - if (handle) { - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - data.active = true; - - moveHandle(eventData, toolType, data, handle, handleDoneMove); - e.stopImmediatePropagation(); - return false; - } - } - } - - // Now check to see if there is a line we can move - // now check to see if we have a tool that we can move - if (toolData && pointNearTool) { - var options = { - deleteIfHandleOutsideImage: true, - preventHandleOutsideImage: false - }; - - for (i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords)) { - $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); - cornerstoneTools.moveAllHandles(e, data, toolData, toolType, options, handleDoneMove); - e.stopImmediatePropagation(); - return false; - } + for (i = 0; i < toolData.data.length; i++) { + data = toolData.data[i]; + if (pointNearTool(element, data, coords)) { + $(element).off('CornerstoneToolsMouseMove', mouseMoveCallback); + cornerstoneTools.moveAllHandles(e, data, toolData, toolType, options, handleDoneMove); + e.stopImmediatePropagation(); + return false; } } } } +} - //****************************************/ - // Cornerstone Methods end - //****************************************/ +//****************************************/ +// Cornerstone Methods end +//****************************************/ - // draw perpendicular line - function drawPerpendicularLine(context, eventData, element, data, color, lineWidth) { +// draw perpendicular line +function drawPerpendicularLine(context, eventData, element, data, color, lineWidth) { + var startX; + var startY; + var endX; + var endY; + if (data.handles.start.x === data.handles.end.x && + data.handles.start.y === data.handles.end.y) { + startX = data.handles.start.x; + startY = data.handles.start.y; + endX = data.handles.end.x; + endY = data.handles.end.y; + } else { // mid point of long-axis line - var mid = { + var mid = { x: (data.handles.start.x + data.handles.end.x) / 2, y: (data.handles.start.y + data.handles.end.y) / 2 }; @@ -854,297 +861,296 @@ import { Viewerbase } from 'meteor/ohif:viewerbase'; var perpendicularLineLength = length / 2; - var startX = mid.x + (perpendicularLineLength / 2) * vectorY; - var startY = mid.y - (perpendicularLineLength / 2) * vectorX; - var endX = mid.x - (perpendicularLineLength / 2) * vectorY; - var endY = mid.y + (perpendicularLineLength / 2) * vectorX; + startX = mid.x + (perpendicularLineLength / 2) * vectorY; + startY = mid.y - (perpendicularLineLength / 2) * vectorX; + endX = mid.x - (perpendicularLineLength / 2) * vectorY; + endY = mid.y + (perpendicularLineLength / 2) * vectorX; + } - if (data.handles.perpendicularStart.locked) { - data.handles.perpendicularStart.x = startX; - data.handles.perpendicularStart.y = startY; - data.handles.perpendicularEnd.x = endX; - data.handles.perpendicularEnd.y = endY; + if (data.handles.perpendicularStart.locked) { + data.handles.perpendicularStart.x = startX; + data.handles.perpendicularStart.y = startY; + data.handles.perpendicularEnd.x = endX; + data.handles.perpendicularEnd.y = endY; + } + + // Draw perpendicular line + var perpendicularStartCanvas = cornerstone.pixelToCanvas(element, data.handles.perpendicularStart); + var perpendicularEndCanvas = cornerstone.pixelToCanvas(element, data.handles.perpendicularEnd); + + context.beginPath(); + context.strokeStyle = color; + context.lineWidth = lineWidth; + context.moveTo(perpendicularStartCanvas.x, perpendicularStartCanvas.y); + context.lineTo(perpendicularEndCanvas.x, perpendicularEndCanvas.y); + context.stroke(); + +} + +function findDottedLinePosition(data) { + var distancesArr = []; + var minDistance; + + // Find distances between handles and textBox + var distanceToStart = cornerstoneMath.point.distance(data.handles.start, data.handles.textBox); + distancesArr.push({ + distance: distanceToStart, + point: data.handles.start + }); + minDistance = distanceToStart; + + var distanceToEnd = cornerstoneMath.point.distance(data.handles.end, data.handles.textBox); + distancesArr.push({ + distance: distanceToEnd, + point: data.handles.end + }); + + minDistance = Math.min(distanceToEnd, minDistance); + + if (data.handles.perpendicularStart.x && data.handles.perpendicularStart.y) { + var distanceToPerpendicularStart = cornerstoneMath.point.distance(data.handles.perpendicularStart, data.handles.textBox); + distancesArr.push({ + distance: distanceToPerpendicularStart, + point: data.handles.perpendicularStart + }); + + minDistance = Math.min(distanceToPerpendicularStart, minDistance); + + } + + if (data.handles.perpendicularEnd.x && data.handles.perpendicularEnd.y) { + var distanceToPerpendicularEnd = cornerstoneMath.point.distance(data.handles.perpendicularEnd, data.handles.textBox); + distancesArr.push({ + distance: distanceToPerpendicularEnd, + point: data.handles.perpendicularEnd + }); + + minDistance = Math.min(distanceToPerpendicularEnd, minDistance); + } + + for (var i = 0; i < distancesArr.length; i++) { + var obj = distancesArr[i]; + if (obj.distance === minDistance) { + return obj.point; + } + } +} + +///////// BEGIN IMAGE RENDERING /////// +function onImageRendered(e, eventData) { + // if we have no toolData for this element, return immediately as there is nothing to do + var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); + if (!toolData) { + return; + } + + // LT-29 Disable Target Measurements when pixel spacing is not available + if (!eventData.image.rowPixelSpacing || !eventData.image.columnPixelSpacing) { + return; + } + + // we have tool data for this element - iterate over each one and draw it + var context = eventData.canvasContext.canvas.getContext('2d'); + context.setTransform(1, 0, 0, 1, 0, 0); + + var color; + var element = eventData.element; + var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); + var config = cornerstoneTools.bidirectional.getConfiguration(); + + for (var i = 0; i < toolData.data.length; i++) { + var data = toolData.data[i]; + var strokeWidth = lineWidth; + + context.save(); + + // configurable shadow from CornerstoneTools + if (config && config.shadow) { + context.shadowColor = config.shadowColor || '#000000'; + context.shadowOffsetX = config.shadowOffsetX || 1; + context.shadowOffsetY = config.shadowOffsetY || 1; } - // Draw perpendicular line - var perpendicularStartCanvas = cornerstone.pixelToCanvas(element, data.handles.perpendicularStart); - var perpendicularEndCanvas = cornerstone.pixelToCanvas(element, data.handles.perpendicularEnd); + if (data.active) { + color = cornerstoneTools.toolColors.getActiveColor(); + } else { + color = cornerstoneTools.toolColors.getToolColor(); + } + + // draw the line + var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start); + var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); + var canvasTextLocation = cornerstone.pixelToCanvas(element, data.handles.textBox); context.beginPath(); context.strokeStyle = color; - context.lineWidth = lineWidth; - context.moveTo(perpendicularStartCanvas.x, perpendicularStartCanvas.y); - context.lineTo(perpendicularEndCanvas.x, perpendicularEndCanvas.y); + context.lineWidth = strokeWidth; + context.moveTo(handleStartCanvas.x, handleStartCanvas.y); + context.lineTo(handleEndCanvas.x, handleEndCanvas.y); context.stroke(); - } + // Draw perpendicular line + drawPerpendicularLine(context, eventData, element, data, color, strokeWidth); - function findDottedLinePosition(data) { - var distancesArr = []; - var minDistance; + // draw the handles + cornerstoneTools.drawHandles(context, eventData, data.handles, color); - // Find distances between handles and textBox - var distanceToStart = cornerstoneMath.point.distance(data.handles.start, data.handles.textBox); - distancesArr.push({ - distance: distanceToStart, - point: data.handles.start - }); - minDistance = distanceToStart; - - var distanceToEnd = cornerstoneMath.point.distance(data.handles.end, data.handles.textBox); - distancesArr.push({ - distance: distanceToEnd, - point: data.handles.end - }); - - minDistance = Math.min(distanceToEnd, minDistance); - - if (data.handles.perpendicularStart.x && data.handles.perpendicularStart.y) { - var distanceToPerpendicularStart = cornerstoneMath.point.distance(data.handles.perpendicularStart, data.handles.textBox); - distancesArr.push({ - distance: distanceToPerpendicularStart, - point: data.handles.perpendicularStart - }); - - minDistance = Math.min(distanceToPerpendicularStart, minDistance); + // Calculate the long axis length + var dx = (data.handles.start.x - data.handles.end.x) * (eventData.image.columnPixelSpacing || 1); + var dy = (data.handles.start.y - data.handles.end.y) * (eventData.image.rowPixelSpacing || 1); + var length = Math.sqrt(dx * dx + dy * dy); + // Calculate the short axis length + var wx = (data.handles.perpendicularStart.x - data.handles.perpendicularEnd.x) * (eventData.image.columnPixelSpacing || 1); + var wy = (data.handles.perpendicularStart.y - data.handles.perpendicularEnd.y) * (eventData.image.rowPixelSpacing || 1); + var width = Math.sqrt(wx * wx + wy * wy); + if (!width) { + width = 0; } - if (data.handles.perpendicularEnd.x && data.handles.perpendicularEnd.y) { - var distanceToPerpendicularEnd = cornerstoneMath.point.distance(data.handles.perpendicularEnd, data.handles.textBox); - distancesArr.push({ - distance: distanceToPerpendicularEnd, - point: data.handles.perpendicularEnd - }); - - minDistance = Math.min(distanceToPerpendicularEnd, minDistance); + // Length is always longer than width + if (width > length) { + var tempW = width; + var tempL = length; + length = tempW; + width = tempL; } - for (var i = 0; i < distancesArr.length; i++) { - var obj = distancesArr[i]; - if (obj.distance === minDistance) { - return obj.point; - } - } - } - - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (!toolData) { - return; - } - - // LT-29 Disable Target Measurements when pixel spacing is not available - if (!eventData.image.rowPixelSpacing || !eventData.image.columnPixelSpacing) { - return; - } - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color; - var element = eventData.element; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var config = cornerstoneTools.bidirectional.getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - var data = toolData.data[i]; - var strokeWidth = lineWidth; - - context.save(); - - // configurable shadow from CornerstoneTools - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; + if (data.measurementNumber) { + // Draw the textbox + var suffix = ' mm'; + if (!eventData.image.rowPixelSpacing || !eventData.image.columnPixelSpacing) { + suffix = ' pixels'; } - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } + var lengthText = ' L ' + length.toFixed(1) + suffix; + var widthText = ' W ' + width.toFixed(1) + suffix; + var textLines = [`Target ${data.measurementNumber}`, lengthText, widthText]; - // draw the line - var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - var canvasTextLocation = cornerstone.pixelToCanvas(element, data.handles.textBox); + var boundingBox = cornerstoneTools.drawTextBox(context, + textLines, + canvasTextLocation.x, canvasTextLocation.y, color, config.textBox); + data.handles.textBox.boundingBox = boundingBox; + + OHIF.cornerstone.repositionTextBox(eventData, data, config.textBox); + + // Draw linked line as dashed + var link = { + start: {}, + end: {} + }; + + var midpointCanvas = { + x: (handleStartCanvas.x + handleEndCanvas.x) / 2, + y: (handleStartCanvas.y + handleEndCanvas.y) / 2, + }; + + var points = [ handleStartCanvas, handleEndCanvas, midpointCanvas ]; + + link.end.x = canvasTextLocation.x; + link.end.y = canvasTextLocation.y; + + link.start = cornerstoneMath.point.findClosestPoint(points, link.end); + + var boundingBoxPoints = [ { + // Top middle point of bounding box + x: boundingBox.left + boundingBox.width / 2, + y: boundingBox.top + }, { + // Left middle point of bounding box + x: boundingBox.left, + y: boundingBox.top + boundingBox.height / 2 + }, { + // Bottom middle point of bounding box + x: boundingBox.left + boundingBox.width / 2, + y: boundingBox.top + boundingBox.height + }, { + // Right middle point of bounding box + x: boundingBox.left + boundingBox.width, + y: boundingBox.top + boundingBox.height / 2 + }, + ]; + + link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); context.beginPath(); context.strokeStyle = color; - context.lineWidth = strokeWidth; - context.moveTo(handleStartCanvas.x, handleStartCanvas.y); - context.lineTo(handleEndCanvas.x, handleEndCanvas.y); + context.lineWidth = lineWidth; + context.setLineDash([ 2, 3 ]); + + context.moveTo(link.start.x, link.start.y); + context.lineTo(link.end.x, link.end.y); context.stroke(); + } - // Draw perpendicular line - drawPerpendicularLine(context, eventData, element, data, color, strokeWidth); + // Set measurement text to show lesion table + data.longestDiameter = length.toFixed(1); + data.shortestDiameter = width.toFixed(1); - // draw the handles - cornerstoneTools.drawHandles(context, eventData, data.handles, color); + context.restore(); + } +} - // Calculate the long axis length - var dx = (data.handles.start.x - data.handles.end.x) * (eventData.image.columnPixelSpacing || 1); - var dy = (data.handles.start.y - data.handles.end.y) * (eventData.image.rowPixelSpacing || 1); - var length = Math.sqrt(dx * dx + dy * dy); +function doubleClickCallback(e, eventData) { + // Prevent other double click handlers from firing after this one + //e.stopImmediatePropagation(); - // Calculate the short axis length - var wx = (data.handles.perpendicularStart.x - data.handles.perpendicularEnd.x) * (eventData.image.columnPixelSpacing || 1); - var wy = (data.handles.perpendicularStart.y - data.handles.perpendicularEnd.y) * (eventData.image.rowPixelSpacing || 1); - var width = Math.sqrt(wx * wx + wy * wy); - if (!width) { - width = 0; - } + var element = eventData.element; + var data; - // Length is always longer than width - if (width > length) { - var tempW = width; - var tempL = length; - length = tempW; - width = tempL; - } - - if (data.measurementNumber) { - // Draw the textbox - var suffix = ' mm'; - if (!eventData.image.rowPixelSpacing || !eventData.image.columnPixelSpacing) { - suffix = ' pixels'; - } - - var lengthText = ' L ' + length.toFixed(1) + suffix; - var widthText = ' W ' + width.toFixed(1) + suffix; - var textLines = [`Target ${data.measurementNumber}`, lengthText, widthText]; - - var boundingBox = cornerstoneTools.drawTextBox(context, - textLines, - canvasTextLocation.x, canvasTextLocation.y, color, config.textBox); - - data.handles.textBox.boundingBox = boundingBox; - - OHIF.cornerstone.repositionTextBox(eventData, data, config.textBox); - - // Draw linked line as dashed - var link = { - start: {}, - end: {} - }; - - var midpointCanvas = { - x: (handleStartCanvas.x + handleEndCanvas.x) / 2, - y: (handleStartCanvas.y + handleEndCanvas.y) / 2, - }; - - var points = [ handleStartCanvas, handleEndCanvas, midpointCanvas ]; - - link.end.x = canvasTextLocation.x; - link.end.y = canvasTextLocation.y; - - link.start = cornerstoneMath.point.findClosestPoint(points, link.end); - - var boundingBoxPoints = [ { - // Top middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top - }, { - // Left middle point of bounding box - x: boundingBox.left, - y: boundingBox.top + boundingBox.height / 2 - }, { - // Bottom middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top + boundingBox.height - }, { - // Right middle point of bounding box - x: boundingBox.left + boundingBox.width, - y: boundingBox.top + boundingBox.height / 2 - }, - ]; - - link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 2, 3 ]); - - context.moveTo(link.start.x, link.start.y); - context.lineTo(link.end.x, link.end.y); - context.stroke(); - } - - // Set measurement text to show lesion table - data.longestDiameter = length.toFixed(1); - data.shortestDiameter = width.toFixed(1); - - context.restore(); + function doneCallback(data, deleteTool) { + if (deleteTool === true) { + cornerstoneTools.removeToolState(element, toolType, data); + cornerstone.updateImage(element); } } - function doubleClickCallback(e, eventData) { - // Prevent other double click handlers from firing after this one - //e.stopImmediatePropagation(); + if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { + return false; + } - var element = eventData.element; - var data; + var config = cornerstoneTools.bidirectional.getConfiguration(); - function doneCallback(data, deleteTool) { - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - cornerstone.updateImage(element); - } - } + var coords = eventData.currentPoints.canvas; + var toolData = cornerstoneTools.getToolState(element, toolType); - if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { + // now check to see if there is a handle we can move + if (!toolData) { + return; + } + + for (var i = 0; i < toolData.data.length; i++) { + data = toolData.data[i]; + if (pointNearTool(element, data, coords)) { + data.active = true; + cornerstone.updateImage(element); + // Allow relabelling via a callback + config.changeMeasurementLocationCallback(data, eventData, doneCallback); + + e.stopImmediatePropagation(); return false; } - - var config = cornerstoneTools.bidirectional.getConfiguration(); - - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); - - // now check to see if there is a handle we can move - if (!toolData) { - return; - } - - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords)) { - data.active = true; - cornerstone.updateImage(element); - // Allow relabelling via a callback - config.changeMeasurementLocationCallback(data, eventData, doneCallback); - - e.stopImmediatePropagation(); - return false; - } - } } +} - // module exports - cornerstoneTools.bidirectional = cornerstoneTools.mouseButtonTool({ - createNewMeasurement: createNewMeasurement, - addNewMeasurement: addNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - mouseDoubleClickCallback: doubleClickCallback, - mouseDownCallback: mouseDownCallback, - mouseMoveCallback: mouseMoveCallback, - toolType: toolType - }); +// module exports +cornerstoneTools.bidirectional = cornerstoneTools.mouseButtonTool({ + createNewMeasurement: createNewMeasurement, + addNewMeasurement: addNewMeasurement, + onImageRendered: onImageRendered, + pointNearTool: pointNearTool, + mouseDoubleClickCallback: doubleClickCallback, + mouseDownCallback: mouseDownCallback, + mouseMoveCallback: mouseMoveCallback, + toolType: toolType +}); - cornerstoneTools.bidirectionalTouch = cornerstoneTools.touchTool({ - createNewMeasurement: createNewMeasurement, - addNewMeasurement: addNewMeasurementTouch, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - }); +cornerstoneTools.bidirectionalTouch = cornerstoneTools.touchTool({ + createNewMeasurement: createNewMeasurement, + addNewMeasurement: addNewMeasurementTouch, + onImageRendered: onImageRendered, + pointNearTool: pointNearTool, + toolType: toolType +}); - cornerstoneTools.bidirectional.setConfiguration(configuration); - -})($, cornerstone, cornerstoneMath, cornerstoneTools); +cornerstoneTools.bidirectional.setConfiguration(configuration); diff --git a/Packages/ohif-lesiontracker/client/compatibility/nonTargetTool.js b/Packages/ohif-lesiontracker/client/compatibility/nonTargetTool.js index d74129cdd..1e4c1678d 100644 --- a/Packages/ohif-lesiontracker/client/compatibility/nonTargetTool.js +++ b/Packages/ohif-lesiontracker/client/compatibility/nonTargetTool.js @@ -1,417 +1,412 @@ import { OHIF } from 'meteor/ohif:core'; import { Viewerbase } from 'meteor/ohif:viewerbase'; -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { +import { cornerstone, cornerstoneMath, cornerstoneTools } from 'meteor/ohif:cornerstone'; - 'use strict'; +const toolType = 'nonTarget'; - const toolType = 'nonTarget'; +const toolDefaultStates = Viewerbase.toolManager.getToolDefaultStates(); +const shadowConfig = toolDefaultStates.shadowConfig; +const textBoxConfig = toolDefaultStates.textBoxConfig; - const toolDefaultStates = Viewerbase.toolManager.getToolDefaultStates(); - const shadowConfig = toolDefaultStates.shadowConfig; - const textBoxConfig = toolDefaultStates.textBoxConfig; +const configuration = { + getMeasurementLocationCallback, + changeMeasurementLocationCallback, + drawHandles: false, + drawHandlesOnHover: true, + arrowFirst: true, + textBox: textBoxConfig, + ...shadowConfig, +}; - const configuration = { - getMeasurementLocationCallback: getMeasurementLocationCallback, - changeMeasurementLocationCallback: changeMeasurementLocationCallback, - drawHandles: false, - drawHandlesOnHover: true, - arrowFirst: true, - textBox: textBoxConfig, - ...shadowConfig, +// Used to cancel tool placement +const keys = { + ESC: 27 +}; +const getPosition = eventData => { + const event = eventData.event; + return { + x: event.clientX, + y: event.clientY }; +}; - // Used to cancel tool placement - const keys = { - ESC: 27 - }; - - const getPosition = eventData => { - const event = eventData.event; - return { - x: event.clientX, - y: event.clientY - }; - }; - - // Define a callback to get your text annotation - // This could be used, e.g. to open a modal - function getMeasurementLocationCallback(measurementData, eventData) { - if (OHIF.lesiontracker.removeMeasurementIfInvalid(measurementData, eventData)) { - return; - } - - delete measurementData.isCreating; - - OHIF.ui.showDialog('dialogNonTargetMeasurement', { - position: getPosition(eventData), - title: 'Select Lesion Location', - element: eventData.element, - measurementData - }); +// Define a callback to get your text annotation +// This could be used, e.g. to open a modal +function getMeasurementLocationCallback(measurementData, eventData) { + if (OHIF.lesiontracker.removeMeasurementIfInvalid(measurementData, eventData)) { + return; } - function changeMeasurementLocationCallback(measurementData, eventData) { - if (OHIF.lesiontracker.removeMeasurementIfInvalid(measurementData, eventData)) { - return; - } + delete measurementData.isCreating; - OHIF.ui.showDialog('dialogNonTargetMeasurement', { - position: getPosition(eventData), - title: 'Change Lesion Location', - element: eventData.element, - measurementData, - edit: true - }); + OHIF.ui.showDialog('dialogNonTargetMeasurement', { + position: getPosition(eventData), + title: 'Select Lesion Location', + element: eventData.element, + measurementData + }); +} + +function changeMeasurementLocationCallback(measurementData, eventData) { + if (OHIF.lesiontracker.removeMeasurementIfInvalid(measurementData, eventData)) { + return; } - /// --- Mouse Tool --- /// - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurement(mouseEventData) { - const element = mouseEventData.element; + OHIF.ui.showDialog('dialogNonTargetMeasurement', { + position: getPosition(eventData), + title: 'Change Lesion Location', + element: eventData.element, + measurementData, + edit: true + }); +} - function doneCallback() { - measurementData.active = true; - cornerstone.updateImage(element); - } - - const measurementData = createNewMeasurement(mouseEventData); - measurementData.viewport = cornerstone.getViewport(element); - - const eventData = { - mouseButtonMask: mouseEventData.which - }; - - const config = cornerstoneTools.nonTarget.getConfiguration(); - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(element).off('CornerstoneToolsMouseMove', cornerstoneTools.nonTarget.mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', cornerstoneTools.nonTarget.mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.nonTarget.mouseDownActivateCallback); - $(element).off('CornerstoneToolsMouseDoubleClick', doubleClickCallback); - - // Add a flag for using Esc to cancel tool placement - let cancelled = false; - function cancelCallback(e) { - // If the Esc key was pressed, set the flag to true - if (e.which === keys.ESC) { - cancelled = true; - cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); - } - - // Don't propagate this keydown event so it can't interfere - // with anything outside of this tool - return false; - } - - // Bind a one-time event listener for the Esc key - $(element).one('keydown', cancelCallback); +/// --- Mouse Tool --- /// +///////// BEGIN ACTIVE TOOL /////// +function addNewMeasurement(mouseEventData) { + const element = mouseEventData.element; + function doneCallback() { + measurementData.active = true; cornerstone.updateImage(element); - - cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { - if (cancelled || cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); - } else { - config.getMeasurementLocationCallback(measurementData, mouseEventData, doneCallback); - } - - // Unbind the Esc keydown hook - $(element).off('keydown', cancelCallback); - - $(element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.nonTarget.mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.nonTarget.mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.nonTarget.mouseDownActivateCallback); - $(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback); - - cornerstone.updateImage(mouseEventData.element); - }); } - function createNewMeasurement(mouseEventData) { - var imageId = mouseEventData.image.imageId; + const measurementData = createNewMeasurement(mouseEventData); + measurementData.viewport = cornerstone.getViewport(element); - // Get studyInstanceUid - var study = cornerstoneTools.metaData.get('study', imageId); - var studyInstanceUid = study.studyInstanceUid; - var patientId = study.patientId; + const eventData = { + mouseButtonMask: mouseEventData.which + }; - // Get seriesInstanceUid - var series = cornerstoneTools.metaData.get('series', imageId); - var seriesInstanceUid = series.seriesInstanceUid; + const config = cornerstoneTools.nonTarget.getConfiguration(); - // create the measurement data for this tool with the end handle activated - var measurementData = { - isCreating: true, - visible: true, - active: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - allowedOutsideImage: true, - highlight: true, - active: false - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - allowedOutsideImage: true, - highlight: true, - active: false - }, - textBox: { - x: mouseEventData.currentPoints.image.x - 50, - y: mouseEventData.currentPoints.image.y - 50, - active: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - } + // associate this data with this imageId so we can render it and manipulate it + cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData); + + // since we are dragging to another place to drop the end point, we can just activate + // the end point and let the moveHandle move it for us. + $(element).off('CornerstoneToolsMouseMove', cornerstoneTools.nonTarget.mouseMoveCallback); + $(element).off('CornerstoneToolsMouseDown', cornerstoneTools.nonTarget.mouseDownCallback); + $(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.nonTarget.mouseDownActivateCallback); + $(element).off('CornerstoneToolsMouseDoubleClick', doubleClickCallback); + + // Add a flag for using Esc to cancel tool placement + let cancelled = false; + function cancelCallback(e) { + // If the Esc key was pressed, set the flag to true + if (e.which === keys.ESC) { + cancelled = true; + cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); + } + + // Don't propagate this keydown event so it can't interfere + // with anything outside of this tool + return false; + } + + // Bind a one-time event listener for the Esc key + $(element).one('keydown', cancelCallback); + + cornerstone.updateImage(element); + + cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { + if (cancelled || cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); + } else { + config.getMeasurementLocationCallback(measurementData, mouseEventData, doneCallback); + } + + // Unbind the Esc keydown hook + $(element).off('keydown', cancelCallback); + + $(element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.nonTarget.mouseMoveCallback); + $(element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.nonTarget.mouseDownCallback); + $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.nonTarget.mouseDownActivateCallback); + $(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback); + + cornerstone.updateImage(mouseEventData.element); + }); +} + +function createNewMeasurement(mouseEventData) { + var imageId = mouseEventData.image.imageId; + + // Get studyInstanceUid + var study = cornerstoneTools.metaData.get('study', imageId); + var studyInstanceUid = study.studyInstanceUid; + var patientId = study.patientId; + + // Get seriesInstanceUid + var series = cornerstoneTools.metaData.get('series', imageId); + var seriesInstanceUid = series.seriesInstanceUid; + + // create the measurement data for this tool with the end handle activated + var measurementData = { + isCreating: true, + visible: true, + active: true, + handles: { + start: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + allowedOutsideImage: true, + highlight: true, + active: false }, - imageId: imageId, - seriesInstanceUid: seriesInstanceUid, - studyInstanceUid: studyInstanceUid, - patientId: patientId, - response: '', - isTarget: false, - toolType: 'nonTarget' - }; + end: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + allowedOutsideImage: true, + highlight: true, + active: false + }, + textBox: { + x: mouseEventData.currentPoints.image.x - 50, + y: mouseEventData.currentPoints.image.y - 50, + active: false, + movesIndependently: false, + drawnIndependently: true, + allowedOutsideImage: true, + hasBoundingBox: true + } + }, + imageId: imageId, + seriesInstanceUid: seriesInstanceUid, + studyInstanceUid: studyInstanceUid, + patientId: patientId, + response: '', + isTarget: false, + toolType: 'nonTarget' + }; - return measurementData; - } - ///////// END ACTIVE TOOL /////// + return measurementData; +} +///////// END ACTIVE TOOL /////// - function pointNearTool(element, data, coords) { - var lineSegment = { - start: cornerstone.pixelToCanvas(element, data.handles.start), - end: cornerstone.pixelToCanvas(element, data.handles.end) - }; - var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); +function pointNearTool(element, data, coords) { + var lineSegment = { + start: cornerstone.pixelToCanvas(element, data.handles.start), + end: cornerstone.pixelToCanvas(element, data.handles.end) + }; + var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - if (cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { - return true; - } - - return distanceToPoint < 25; + if (cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { + return true; } - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - var element = eventData.element; + return distanceToPoint < 25; +} - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(element, toolType); - if (!toolData) { - return; - } +///////// BEGIN IMAGE RENDERING /////// +function onImageRendered(e, eventData) { + var element = eventData.element; - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var config = cornerstoneTools.nonTarget.getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - var data = toolData.data[i]; - - context.save(); - - // configurable shadow from CornerstoneTools - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } - - // Draw the arrow - var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - var canvasTextLocation = cornerstone.pixelToCanvas(element, data.handles.textBox); - - cornerstoneTools.drawArrow(context, handleEndCanvas, handleStartCanvas, color, lineWidth); - - if (config.drawHandles) { - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - } else if (config.drawHandlesOnHover && data.handles.start.active) { - cornerstoneTools.drawHandles(context, eventData, [ data.handles.start ], color); - } else if (config.drawHandlesOnHover && data.handles.end.active) { - cornerstoneTools.drawHandles(context, eventData, [ data.handles.end ], color); - } - - // Draw the text - if (data.measurementNumber) { - - var textLine = `Non-Target ${data.measurementNumber}`; - var boundingBox = cornerstoneTools.drawTextBox(context, textLine, canvasTextLocation.x, canvasTextLocation.y, color, config.textBox); - data.handles.textBox.boundingBox = boundingBox; - - OHIF.cornerstone.repositionTextBox(eventData, data, config.textBox); - - // Draw linked line as dashed - var link = { - start: {}, - end: {} - }; - - var midpointCanvas = { - x: (handleStartCanvas.x + handleEndCanvas.x) / 2, - y: (handleStartCanvas.y + handleEndCanvas.y) / 2, - }; - - var points = [ handleStartCanvas, handleEndCanvas, midpointCanvas ]; - - link.end.x = canvasTextLocation.x; - link.end.y = canvasTextLocation.y; - - link.start = cornerstoneMath.point.findClosestPoint(points, link.end); - - var boundingBoxPoints = [ { - // Top middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top - }, { - // Left middle point of bounding box - x: boundingBox.left, - y: boundingBox.top + boundingBox.height / 2 - }, { - // Bottom middle point of bounding box - x: boundingBox.left + boundingBox.width / 2, - y: boundingBox.top + boundingBox.height - }, { - // Right middle point of bounding box - x: boundingBox.left + boundingBox.width, - y: boundingBox.top + boundingBox.height / 2 - }, - ]; - - link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([ 2, 3 ]); - - context.moveTo(link.start.x, link.start.y); - context.lineTo(link.end.x, link.end.y); - context.stroke(); - } - - context.restore(); - } + // if we have no toolData for this element, return immediately as there is nothing to do + var toolData = cornerstoneTools.getToolState(element, toolType); + if (!toolData) { + return; } - // ---- Touch tool ---- + // we have tool data for this element - iterate over each one and draw it + var context = eventData.canvasContext.canvas.getContext('2d'); + context.setTransform(1, 0, 0, 1, 0, 0); - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurementTouch(touchEventData) { - var element = touchEventData.element; + var color; + var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); + var config = cornerstoneTools.nonTarget.getConfiguration(); - function doneCallback() { - measurementData.active = true; - cornerstone.updateImage(element); + for (var i = 0; i < toolData.data.length; i++) { + var data = toolData.data[i]; + + context.save(); + + // configurable shadow from CornerstoneTools + if (config && config.shadow) { + context.shadowColor = config.shadowColor || '#000000'; + context.shadowOffsetX = config.shadowOffsetX || 1; + context.shadowOffsetY = config.shadowOffsetY || 1; } - var measurementData = createNewMeasurement(touchEventData); - cornerstoneTools.addToolState(element, toolType, measurementData); - $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.nonTargetTouch.touchMoveHandle); - $(element).off('CornerstoneToolsDragStartActive', cornerstoneTools.nonTargetTouch.touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools.nonTargetTouch.tapCallback); - var config = cornerstoneTools.nonTarget.getConfiguration(); + if (data.active) { + color = cornerstoneTools.toolColors.getActiveColor(); + } else { + color = cornerstoneTools.toolColors.getToolColor(); + } + // Draw the arrow + var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start); + var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); + var canvasTextLocation = cornerstone.pixelToCanvas(element, data.handles.textBox); + + cornerstoneTools.drawArrow(context, handleEndCanvas, handleStartCanvas, color, lineWidth); + + if (config.drawHandles) { + cornerstoneTools.drawHandles(context, eventData, data.handles, color); + } else if (config.drawHandlesOnHover && data.handles.start.active) { + cornerstoneTools.drawHandles(context, eventData, [ data.handles.start ], color); + } else if (config.drawHandlesOnHover && data.handles.end.active) { + cornerstoneTools.drawHandles(context, eventData, [ data.handles.end ], color); + } + + // Draw the text + if (data.measurementNumber) { + + var textLine = `Non-Target ${data.measurementNumber}`; + var boundingBox = cornerstoneTools.drawTextBox(context, textLine, canvasTextLocation.x, canvasTextLocation.y, color, config.textBox); + data.handles.textBox.boundingBox = boundingBox; + + OHIF.cornerstone.repositionTextBox(eventData, data, config.textBox); + + // Draw linked line as dashed + var link = { + start: {}, + end: {} + }; + + var midpointCanvas = { + x: (handleStartCanvas.x + handleEndCanvas.x) / 2, + y: (handleStartCanvas.y + handleEndCanvas.y) / 2, + }; + + var points = [ handleStartCanvas, handleEndCanvas, midpointCanvas ]; + + link.end.x = canvasTextLocation.x; + link.end.y = canvasTextLocation.y; + + link.start = cornerstoneMath.point.findClosestPoint(points, link.end); + + var boundingBoxPoints = [ { + // Top middle point of bounding box + x: boundingBox.left + boundingBox.width / 2, + y: boundingBox.top + }, { + // Left middle point of bounding box + x: boundingBox.left, + y: boundingBox.top + boundingBox.height / 2 + }, { + // Bottom middle point of bounding box + x: boundingBox.left + boundingBox.width / 2, + y: boundingBox.top + boundingBox.height + }, { + // Right middle point of bounding box + x: boundingBox.left + boundingBox.width, + y: boundingBox.top + boundingBox.height / 2 + }, + ]; + + link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); + context.beginPath(); + context.strokeStyle = color; + context.lineWidth = lineWidth; + context.setLineDash([ 2, 3 ]); + + context.moveTo(link.start.x, link.start.y); + context.lineTo(link.end.x, link.end.y); + context.stroke(); + } + + context.restore(); + } +} + +// ---- Touch tool ---- + +///////// BEGIN ACTIVE TOOL /////// +function addNewMeasurementTouch(touchEventData) { + var element = touchEventData.element; + + function doneCallback() { + measurementData.active = true; + cornerstone.updateImage(element); + } + + var measurementData = createNewMeasurement(touchEventData); + cornerstoneTools.addToolState(element, toolType, measurementData); + $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.nonTargetTouch.touchMoveHandle); + $(element).off('CornerstoneToolsDragStartActive', cornerstoneTools.nonTargetTouch.touchDownActivateCallback); + $(element).off('CornerstoneToolsTap', cornerstoneTools.nonTargetTouch.tapCallback); + var config = cornerstoneTools.nonTarget.getConfiguration(); + + cornerstone.updateImage(element); + + cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() { cornerstone.updateImage(element); - cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() { - cornerstone.updateImage(element); - - if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - config.getMeasurementLocationCallback(measurementData, touchEventData, doneCallback); - - $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.nonTargetTouch.touchMoveHandle); - $(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.nonTargetTouch.touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', cornerstoneTools.nonTargetTouch.tapCallback); - }); - } - - function doubleClickCallback(e, eventData) { - var element = eventData.element; - var data; - - function doneCallback(data, deleteTool) { - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - cornerstone.updateImage(element); - return; - } - - data.active = false; - cornerstone.updateImage(element); + if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(element, toolType, measurementData); } - if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - return false; - } + config.getMeasurementLocationCallback(measurementData, touchEventData, doneCallback); - var config = cornerstoneTools.nonTarget.getConfiguration(); + $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.nonTargetTouch.touchMoveHandle); + $(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.nonTargetTouch.touchDownActivateCallback); + $(element).on('CornerstoneToolsTap', cornerstoneTools.nonTargetTouch.tapCallback); + }); +} - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); +function doubleClickCallback(e, eventData) { + var element = eventData.element; + var data; - // now check to see if there is a handle we can move - if (!toolData) { + function doneCallback(data, deleteTool) { + if (deleteTool === true) { + cornerstoneTools.removeToolState(element, toolType, data); + cornerstone.updateImage(element); return; } - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords)) { - data.active = true; - cornerstone.updateImage(element); - // Allow relabelling via a callback - config.changeMeasurementLocationCallback(data, eventData, doneCallback); - - e.stopImmediatePropagation(); - return false; - } - } + data.active = false; + cornerstone.updateImage(element); } - cornerstoneTools.nonTarget = cornerstoneTools.mouseButtonTool({ - addNewMeasurement: addNewMeasurement, - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType, - mouseDoubleClickCallback: doubleClickCallback - }); + if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { + return false; + } - cornerstoneTools.nonTarget.setConfiguration(configuration); + var config = cornerstoneTools.nonTarget.getConfiguration(); - cornerstoneTools.nonTargetTouch = cornerstoneTools.touchTool({ - addNewMeasurement: addNewMeasurementTouch, - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - // pressCallback: doubleClickCallback - }); + var coords = eventData.currentPoints.canvas; + var toolData = cornerstoneTools.getToolState(element, toolType); -})($, cornerstone, cornerstoneMath, cornerstoneTools); + // now check to see if there is a handle we can move + if (!toolData) { + return; + } + + for (var i = 0; i < toolData.data.length; i++) { + data = toolData.data[i]; + if (pointNearTool(element, data, coords)) { + data.active = true; + cornerstone.updateImage(element); + // Allow relabelling via a callback + config.changeMeasurementLocationCallback(data, eventData, doneCallback); + + e.stopImmediatePropagation(); + return false; + } + } +} + +cornerstoneTools.nonTarget = cornerstoneTools.mouseButtonTool({ + addNewMeasurement, + createNewMeasurement, + onImageRendered, + pointNearTool, + toolType, + mouseDoubleClickCallback: doubleClickCallback +}); + +cornerstoneTools.nonTarget.setConfiguration(configuration); + +cornerstoneTools.nonTargetTouch = cornerstoneTools.touchTool({ + addNewMeasurement: addNewMeasurementTouch, + createNewMeasurement, + onImageRendered, + pointNearTool, + toolType + // pressCallback: doubleClickCallback +}); diff --git a/Packages/ohif-lesiontracker/client/compatibility/qualitativeTargetTools.js b/Packages/ohif-lesiontracker/client/compatibility/qualitativeTargetTools.js index a8839179a..41788087d 100644 --- a/Packages/ohif-lesiontracker/client/compatibility/qualitativeTargetTools.js +++ b/Packages/ohif-lesiontracker/client/compatibility/qualitativeTargetTools.js @@ -1,424 +1,420 @@ import { Viewerbase } from 'meteor/ohif:viewerbase'; +import { cornerstone, cornerstoneMath, cornerstoneTools } from 'meteor/ohif:cornerstone'; -(function($, cornerstone, cornerstoneMath, cornerstoneTools) { +var responseByToolType = [{ + "toolType": "targetCR", + "toolResponse": "CR" +}, { + "toolType": "targetUN", + "toolResponse": "UN" +}, { + "toolType": "targetEX", + "toolResponse": "EX" +}]; - 'use strict'; - var responseByToolType = [{ - "toolType": "targetCR", - "toolResponse": "CR" - }, { - "toolType": "targetUN", - "toolResponse": "UN" - }, { - "toolType": "targetEX", - "toolResponse": "EX" - }]; +const toolDefaultStates = Viewerbase.toolManager.getToolDefaultStates(); +const textBoxConfig = toolDefaultStates.textBoxConfig; - const toolDefaultStates = Viewerbase.toolManager.getToolDefaultStates(); - const textBoxConfig = toolDefaultStates.textBoxConfig; +var configuration = { + getMeasurementLocationCallback, + changeMeasurementLocationCallback, + drawHandles: false, + drawHandlesOnHover: false, + arrowFirst: true, + textBox: textBoxConfig +}; - var configuration = { - getMeasurementLocationCallback: getMeasurementLocationCallback, - changeMeasurementLocationCallback: changeMeasurementLocationCallback, - drawHandles: false, - drawHandlesOnHover: false, - arrowFirst: true, - textBox: textBoxConfig +// Used to cancel tool placement +var keys = { + ESC: 27 +}; + +// Define a callback to get your text annotation +// This could be used, e.g. to open a modal +function getMeasurementLocationCallback(measurementData, eventData, doneCallback) { + doneCallback(prompt('Enter your lesion location:')); +} + +function changeMeasurementLocationCallback(measurementData, eventData, doneCallback) { + doneCallback(prompt('Change your lesion location:')); +} + + +function createQualitativeTargetTool(toolType) { + var toolInterface = { + toolType: toolType }; - // Used to cancel tool placement - var keys = { - ESC: 27 - }; + var response; - // Define a callback to get your text annotation - // This could be used, e.g. to open a modal - function getMeasurementLocationCallback(measurementData, eventData, doneCallback) { - doneCallback(prompt('Enter your lesion location:')); + responseByToolType.forEach(function(tool) { + if (tool.toolType === toolInterface.toolType) { + response = tool.toolResponse; + } + }); + + /// --- Mouse Tool --- /// + ///////// BEGIN ACTIVE TOOL /////// + function addNewMeasurement(mouseEventData) { + var element = mouseEventData.element; + + function doneCallback() { + measurementData.active = true; + cornerstone.updateImage(element); + } + + var measurementData = createNewMeasurement(mouseEventData); + + var eventData = { + mouseButtonMask: mouseEventData.which + }; + + var config = cornerstoneTools[toolType].getConfiguration(); + + // associate this data with this imageId so we can render it and manipulate it + cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData); + + // since we are dragging to another place to drop the end point, we can just activate + // the end point and let the moveHandle move it for us. + $(element).off('CornerstoneToolsMouseMove', cornerstoneTools[toolType].mouseMoveCallback); + $(element).off('CornerstoneToolsMouseDown', cornerstoneTools[toolType].mouseDownCallback); + $(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools[toolType].mouseDownActivateCallback); + $(element).off('CornerstoneToolsMouseDoubleClick', doubleClickCallback); + + // Add a flag for using Esc to cancel tool placement + var cancelled = false; + + function cancelCallback(e) { + // If the Esc key was pressed, set the flag to true + if (e.which === keys.ESC) { + cancelled = true; + cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); + } + + // Don't propagate this keydown event so it can't interfere + // with anything outside of this tool + return false; + } + + // Bind a one-time event listener for the Esc key + $(element).one('keydown', cancelCallback); + + cornerstone.updateImage(element); + + cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { + if (cancelled || cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); + } else { + config.getMeasurementLocationCallback(measurementData, mouseEventData, doneCallback); + } + + // Unbind the Esc keydown hook + $(element).off('keydown', cancelCallback); + + $(element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools[toolType].mouseMoveCallback); + $(element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools[toolType].mouseDownCallback); + $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools[toolType].mouseDownActivateCallback); + $(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback); + + $(element).off('keydown', cancelCallback); + + cornerstone.updateImage(mouseEventData.element); + }); } - function changeMeasurementLocationCallback(measurementData, eventData, doneCallback) { - doneCallback(prompt('Change your lesion location:')); - } + function createNewMeasurement(mouseEventData) { + var imageId = mouseEventData.image.imageId; + // Get studyInstanceUid + var study = cornerstoneTools.metaData.get('study', imageId); + var studyInstanceUid = study.studyInstanceUid; + var patientId = study.patientId; - function createQualitativeTargetTool(toolType) { - var toolInterface = { + // Get seriesInstanceUid + var series = cornerstoneTools.metaData.get('series', imageId); + var seriesInstanceUid = series.seriesInstanceUid; + + // create the measurement data for this tool with the end handle activated + var measurementData = { + visible: true, + active: true, + handles: { + start: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + allowedOutsideImage: true, + highlight: true, + active: false + }, + end: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + allowedOutsideImage: true, + highlight: true, + active: false + }, + textBox: { + x: mouseEventData.currentPoints.image.x - 50, + y: mouseEventData.currentPoints.image.y - 50, + active: false, + movesIndependently: false, + drawnIndependently: true, + allowedOutsideImage: true, + hasBoundingBox: true + } + }, + imageId: imageId, + seriesInstanceUid: seriesInstanceUid, + studyInstanceUid: studyInstanceUid, + patientId: patientId, + response: response, + isTarget: true, toolType: toolType }; - var response; + return measurementData; + } + ///////// END ACTIVE TOOL /////// - responseByToolType.forEach(function(tool) { - if (tool.toolType === toolInterface.toolType) { - response = tool.toolResponse; - } - }); + function pointNearTool(element, data, coords) { + var lineSegment = { + start: cornerstone.pixelToCanvas(element, data.handles.start), + end: cornerstone.pixelToCanvas(element, data.handles.end) + }; + var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - /// --- Mouse Tool --- /// - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurement(mouseEventData) { - var element = mouseEventData.element; - - function doneCallback() { - measurementData.active = true; - cornerstone.updateImage(element); - } - - var measurementData = createNewMeasurement(mouseEventData); - - var eventData = { - mouseButtonMask: mouseEventData.which - }; - - var config = cornerstoneTools[toolType].getConfiguration(); - - // associate this data with this imageId so we can render it and manipulate it - cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData); - - // since we are dragging to another place to drop the end point, we can just activate - // the end point and let the moveHandle move it for us. - $(element).off('CornerstoneToolsMouseMove', cornerstoneTools[toolType].mouseMoveCallback); - $(element).off('CornerstoneToolsMouseDown', cornerstoneTools[toolType].mouseDownCallback); - $(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools[toolType].mouseDownActivateCallback); - $(element).off('CornerstoneToolsMouseDoubleClick', doubleClickCallback); - - // Add a flag for using Esc to cancel tool placement - var cancelled = false; - - function cancelCallback(e) { - // If the Esc key was pressed, set the flag to true - if (e.which === keys.ESC) { - cancelled = true; - cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); - } - - // Don't propagate this keydown event so it can't interfere - // with anything outside of this tool - return false; - } - - // Bind a one-time event listener for the Esc key - $(element).one('keydown', cancelCallback); - - cornerstone.updateImage(element); - - cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { - if (cancelled || cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData); - } else { - config.getMeasurementLocationCallback(measurementData, mouseEventData, doneCallback); - } - - // Unbind the Esc keydown hook - $(element).off('keydown', cancelCallback); - - $(element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools[toolType].mouseMoveCallback); - $(element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools[toolType].mouseDownCallback); - $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools[toolType].mouseDownActivateCallback); - $(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback); - - $(element).off('keydown', cancelCallback); - - cornerstone.updateImage(mouseEventData.element); - }); + if (cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { + return true; } - function createNewMeasurement(mouseEventData) { - var imageId = mouseEventData.image.imageId; - - // Get studyInstanceUid - var study = cornerstoneTools.metaData.get('study', imageId); - var studyInstanceUid = study.studyInstanceUid; - var patientId = study.patientId; - - // Get seriesInstanceUid - var series = cornerstoneTools.metaData.get('series', imageId); - var seriesInstanceUid = series.seriesInstanceUid; - - // create the measurement data for this tool with the end handle activated - var measurementData = { - visible: true, - active: true, - handles: { - start: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - allowedOutsideImage: true, - highlight: true, - active: false - }, - end: { - x: mouseEventData.currentPoints.image.x, - y: mouseEventData.currentPoints.image.y, - allowedOutsideImage: true, - highlight: true, - active: false - }, - textBox: { - x: mouseEventData.currentPoints.image.x - 50, - y: mouseEventData.currentPoints.image.y - 50, - active: false, - movesIndependently: false, - drawnIndependently: true, - allowedOutsideImage: true, - hasBoundingBox: true - } - }, - imageId: imageId, - seriesInstanceUid: seriesInstanceUid, - studyInstanceUid: studyInstanceUid, - patientId: patientId, - response: response, - isTarget: true, - toolType: toolType - }; - - return measurementData; - } - ///////// END ACTIVE TOOL /////// - - function pointNearTool(element, data, coords) { - var lineSegment = { - start: cornerstone.pixelToCanvas(element, data.handles.start), - end: cornerstone.pixelToCanvas(element, data.handles.end) - }; - var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); - - if (cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { - return true; - } - - return distanceToPoint < 25; - } - - function drawDottedArrow(context, start, end, color, lineWidth) { - //variables to be used when creating the arrow - var headLength = 10; - - var angle = Math.atan2(end.y - start.y, end.x - start.x); - - //starting path of the arrow from the start square to the end square and drawing the stroke - context.beginPath(); - context.moveTo(start.x, start.y); - context.lineTo(end.x, end.y); - context.strokeStyle = color; - context.lineWidth = lineWidth; - //context.setLineDash([ 2, 3 ]); - context.stroke(); - - //starting a new path from the head of the arrow to one of the sides of the point - context.beginPath(); - context.moveTo(end.x, end.y); - context.lineTo(end.x - headLength * Math.cos(angle - Math.PI / 7), end.y - headLength * Math.sin(angle - Math.PI / 7)); - - //path from the side point of the arrow, to the other side point - context.lineTo(end.x - headLength * Math.cos(angle + Math.PI / 7), end.y - headLength * Math.sin(angle + Math.PI / 7)); - - //path from the side point back to the tip of the arrow, and then again to the opposite side point - context.lineTo(end.x, end.y); - context.lineTo(end.x - headLength * Math.cos(angle - Math.PI / 7), end.y - headLength * Math.sin(angle - Math.PI / 7)); - - //draws the paths created above - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.stroke(); - context.fillStyle = color; - context.fill(); - } - - ///////// BEGIN IMAGE RENDERING /////// - function onImageRendered(e, eventData) { - var element = eventData.element; - - // if we have no toolData for this element, return immediately as there is nothing to do - var toolData = cornerstoneTools.getToolState(element, toolType); - if (!toolData) { - return; - } - - // we have tool data for this element - iterate over each one and draw it - var context = eventData.canvasContext.canvas.getContext('2d'); - context.setTransform(1, 0, 0, 1, 0, 0); - - var color; - var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var config = cornerstoneTools[toolType].getConfiguration(); - - for (var i = 0; i < toolData.data.length; i++) { - var data = toolData.data[i]; - - context.save(); - - // configurable shadow from CornerstoneTools - if (config && config.shadow) { - context.shadowColor = config.shadowColor || '#000000'; - context.shadowOffsetX = config.shadowOffsetX || 1; - context.shadowOffsetY = config.shadowOffsetY || 1; - } - - if (data.active) { - color = cornerstoneTools.toolColors.getActiveColor(); - } else { - color = cornerstoneTools.toolColors.getToolColor(); - } - - // Draw the arrow - var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start); - var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); - var canvasTextLocation = cornerstone.pixelToCanvas(element, data.handles.textBox); - - drawDottedArrow(context, handleEndCanvas, handleStartCanvas, color, lineWidth); - - if (config.drawHandles) { - cornerstoneTools.drawHandles(context, eventData, data.handles, color); - } else if (config.drawHandlesOnHover && data.handles.start.active) { - cornerstoneTools.drawHandles(context, eventData, [data.handles.start], color); - } else if (config.drawHandlesOnHover && data.handles.end.active) { - cornerstoneTools.drawHandles(context, eventData, [data.handles.end], color); - } - - // Draw the text - if (data.measurementNumber) { - // Draw linked line as dashed - var mid = { - x: (handleStartCanvas.x + handleEndCanvas.x) / 2, - y: (handleStartCanvas.y + handleEndCanvas.y) / 2 - }; - - context.beginPath(); - context.strokeStyle = color; - context.lineWidth = lineWidth; - context.setLineDash([2, 3]); - - context.moveTo(mid.x, mid.y); - context.lineTo(canvasTextLocation.x + 20, canvasTextLocation.y + 20); - context.stroke(); - - var textLine = `Target ${data.measurementNumber}`; - var boundingBox = cornerstoneTools.drawTextBox(context, textLine, canvasTextLocation.x, canvasTextLocation.y, color, config.textBox); - data.handles.textBox.boundingBox = boundingBox; - } - - context.restore(); - } - } - - // ---- Touch tool ---- - - ///////// BEGIN ACTIVE TOOL /////// - function addNewMeasurementTouch(touchEventData) { - var element = touchEventData.element; - - function doneCallback() { - measurementData.active = true; - cornerstone.updateImage(element); - } - - var measurementData = createNewMeasurement(touchEventData); - cornerstoneTools.addToolState(element, toolType, measurementData); - $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools[toolType + "Touch"].touchMoveHandle); - $(element).off('CornerstoneToolsDragStartActive', cornerstoneTools[toolType + "Touch"].touchDownActivateCallback); - $(element).off('CornerstoneToolsTap', cornerstoneTools[toolType + "Touch"].tapCallback); - var config = cornerstoneTools[toolType].getConfiguration(); - - cornerstone.updateImage(element); - - cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() { - cornerstone.updateImage(element); - - if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { - // delete the measurement - cornerstoneTools.removeToolState(element, toolType, measurementData); - } - - config.getMeasurementLocationCallback(measurementData, touchEventData, doneCallback); - - $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools[toolType + "Touch"].touchMoveHandle); - $(element).on('CornerstoneToolsDragStartActive', cornerstoneTools[toolType + "Touch"].touchDownActivateCallback); - $(element).on('CornerstoneToolsTap', cornerstoneTools[toolType + "Touch"].tapCallback); - }); - } - - function doubleClickCallback(e, eventData) { - var element = eventData.element; - var data; - - function doneCallback(data, deleteTool) { - if (deleteTool === true) { - cornerstoneTools.removeToolState(element, toolType, data); - cornerstone.updateImage(element); - return; - } - - data.active = false; - cornerstone.updateImage(element); - } - - if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { - return false; - } - - var config = cornerstoneTools[toolType].getConfiguration(); - - var coords = eventData.currentPoints.canvas; - var toolData = cornerstoneTools.getToolState(element, toolType); - - // now check to see if there is a handle we can move - if (!toolData) { - return; - } - - for (var i = 0; i < toolData.data.length; i++) { - data = toolData.data[i]; - if (pointNearTool(element, data, coords)) { - data.active = true; - cornerstone.updateImage(element); - // Allow relabelling via a callback - config.changeMeasurementLocationCallback(data, eventData, doneCallback); - - e.stopImmediatePropagation(); - return false; - } - } - } - - - toolInterface.mouse = cornerstoneTools.mouseButtonTool({ - addNewMeasurement: addNewMeasurement, - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType, - mouseDoubleClickCallback: doubleClickCallback - }); - - toolInterface.touch = cornerstoneTools.touchTool({ - addNewMeasurement: addNewMeasurementTouch, - createNewMeasurement: createNewMeasurement, - onImageRendered: onImageRendered, - pointNearTool: pointNearTool, - toolType: toolType - // pressCallback: doubleClickCallback - }); - - return toolInterface; + return distanceToPoint < 25; } - var targetCRInterface = createQualitativeTargetTool('targetCR'); - cornerstoneTools.targetCR = targetCRInterface.mouse; - cornerstoneTools.targetCR.setConfiguration(configuration); - cornerstoneTools.targetCRTouch = targetCRInterface.touch; + function drawDottedArrow(context, start, end, color, lineWidth) { + //variables to be used when creating the arrow + var headLength = 10; - var targetUNInterface = createQualitativeTargetTool('targetUN'); - cornerstoneTools.targetUN = targetUNInterface.mouse; - cornerstoneTools.targetUN.setConfiguration(configuration); - cornerstoneTools.targetUNTouch = targetUNInterface.touch; + var angle = Math.atan2(end.y - start.y, end.x - start.x); - var targetEXInterface = createQualitativeTargetTool('targetEX'); - cornerstoneTools.targetEX = targetEXInterface.mouse; - cornerstoneTools.targetEX.setConfiguration(configuration); - cornerstoneTools.targetEXTouch = targetEXInterface.touch; + //starting path of the arrow from the start square to the end square and drawing the stroke + context.beginPath(); + context.moveTo(start.x, start.y); + context.lineTo(end.x, end.y); + context.strokeStyle = color; + context.lineWidth = lineWidth; + //context.setLineDash([ 2, 3 ]); + context.stroke(); -})($, cornerstone, cornerstoneMath, cornerstoneTools); + //starting a new path from the head of the arrow to one of the sides of the point + context.beginPath(); + context.moveTo(end.x, end.y); + context.lineTo(end.x - headLength * Math.cos(angle - Math.PI / 7), end.y - headLength * Math.sin(angle - Math.PI / 7)); + + //path from the side point of the arrow, to the other side point + context.lineTo(end.x - headLength * Math.cos(angle + Math.PI / 7), end.y - headLength * Math.sin(angle + Math.PI / 7)); + + //path from the side point back to the tip of the arrow, and then again to the opposite side point + context.lineTo(end.x, end.y); + context.lineTo(end.x - headLength * Math.cos(angle - Math.PI / 7), end.y - headLength * Math.sin(angle - Math.PI / 7)); + + //draws the paths created above + context.strokeStyle = color; + context.lineWidth = lineWidth; + context.stroke(); + context.fillStyle = color; + context.fill(); + } + + ///////// BEGIN IMAGE RENDERING /////// + function onImageRendered(e, eventData) { + var element = eventData.element; + + // if we have no toolData for this element, return immediately as there is nothing to do + var toolData = cornerstoneTools.getToolState(element, toolType); + if (!toolData) { + return; + } + + // we have tool data for this element - iterate over each one and draw it + var context = eventData.canvasContext.canvas.getContext('2d'); + context.setTransform(1, 0, 0, 1, 0, 0); + + var color; + var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); + var config = cornerstoneTools[toolType].getConfiguration(); + + for (var i = 0; i < toolData.data.length; i++) { + var data = toolData.data[i]; + + context.save(); + + // configurable shadow from CornerstoneTools + if (config && config.shadow) { + context.shadowColor = config.shadowColor || '#000000'; + context.shadowOffsetX = config.shadowOffsetX || 1; + context.shadowOffsetY = config.shadowOffsetY || 1; + } + + if (data.active) { + color = cornerstoneTools.toolColors.getActiveColor(); + } else { + color = cornerstoneTools.toolColors.getToolColor(); + } + + // Draw the arrow + var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start); + var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); + var canvasTextLocation = cornerstone.pixelToCanvas(element, data.handles.textBox); + + drawDottedArrow(context, handleEndCanvas, handleStartCanvas, color, lineWidth); + + if (config.drawHandles) { + cornerstoneTools.drawHandles(context, eventData, data.handles, color); + } else if (config.drawHandlesOnHover && data.handles.start.active) { + cornerstoneTools.drawHandles(context, eventData, [data.handles.start], color); + } else if (config.drawHandlesOnHover && data.handles.end.active) { + cornerstoneTools.drawHandles(context, eventData, [data.handles.end], color); + } + + // Draw the text + if (data.measurementNumber) { + // Draw linked line as dashed + var mid = { + x: (handleStartCanvas.x + handleEndCanvas.x) / 2, + y: (handleStartCanvas.y + handleEndCanvas.y) / 2 + }; + + context.beginPath(); + context.strokeStyle = color; + context.lineWidth = lineWidth; + context.setLineDash([2, 3]); + + context.moveTo(mid.x, mid.y); + context.lineTo(canvasTextLocation.x + 20, canvasTextLocation.y + 20); + context.stroke(); + + var textLine = `Target ${data.measurementNumber}`; + var boundingBox = cornerstoneTools.drawTextBox(context, textLine, canvasTextLocation.x, canvasTextLocation.y, color, config.textBox); + data.handles.textBox.boundingBox = boundingBox; + } + + context.restore(); + } + } + + // ---- Touch tool ---- + + ///////// BEGIN ACTIVE TOOL /////// + function addNewMeasurementTouch(touchEventData) { + var element = touchEventData.element; + + function doneCallback() { + measurementData.active = true; + cornerstone.updateImage(element); + } + + var measurementData = createNewMeasurement(touchEventData); + cornerstoneTools.addToolState(element, toolType, measurementData); + $(element).off('CornerstoneToolsTouchDrag', cornerstoneTools[toolType + "Touch"].touchMoveHandle); + $(element).off('CornerstoneToolsDragStartActive', cornerstoneTools[toolType + "Touch"].touchDownActivateCallback); + $(element).off('CornerstoneToolsTap', cornerstoneTools[toolType + "Touch"].tapCallback); + var config = cornerstoneTools[toolType].getConfiguration(); + + cornerstone.updateImage(element); + + cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() { + cornerstone.updateImage(element); + + if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(element, toolType, measurementData); + } + + config.getMeasurementLocationCallback(measurementData, touchEventData, doneCallback); + + $(element).on('CornerstoneToolsTouchDrag', cornerstoneTools[toolType + "Touch"].touchMoveHandle); + $(element).on('CornerstoneToolsDragStartActive', cornerstoneTools[toolType + "Touch"].touchDownActivateCallback); + $(element).on('CornerstoneToolsTap', cornerstoneTools[toolType + "Touch"].tapCallback); + }); + } + + function doubleClickCallback(e, eventData) { + var element = eventData.element; + var data; + + function doneCallback(data, deleteTool) { + if (deleteTool === true) { + cornerstoneTools.removeToolState(element, toolType, data); + cornerstone.updateImage(element); + return; + } + + data.active = false; + cornerstone.updateImage(element); + } + + if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { + return false; + } + + var config = cornerstoneTools[toolType].getConfiguration(); + + var coords = eventData.currentPoints.canvas; + var toolData = cornerstoneTools.getToolState(element, toolType); + + // now check to see if there is a handle we can move + if (!toolData) { + return; + } + + for (var i = 0; i < toolData.data.length; i++) { + data = toolData.data[i]; + if (pointNearTool(element, data, coords)) { + data.active = true; + cornerstone.updateImage(element); + // Allow relabelling via a callback + config.changeMeasurementLocationCallback(data, eventData, doneCallback); + + e.stopImmediatePropagation(); + return false; + } + } + } + + + toolInterface.mouse = cornerstoneTools.mouseButtonTool({ + addNewMeasurement: addNewMeasurement, + createNewMeasurement: createNewMeasurement, + onImageRendered: onImageRendered, + pointNearTool: pointNearTool, + toolType: toolType, + mouseDoubleClickCallback: doubleClickCallback + }); + + toolInterface.touch = cornerstoneTools.touchTool({ + addNewMeasurement: addNewMeasurementTouch, + createNewMeasurement: createNewMeasurement, + onImageRendered: onImageRendered, + pointNearTool: pointNearTool, + toolType: toolType + // pressCallback: doubleClickCallback + }); + + return toolInterface; +} + +var targetCRInterface = createQualitativeTargetTool('targetCR'); +cornerstoneTools.targetCR = targetCRInterface.mouse; +cornerstoneTools.targetCR.setConfiguration(configuration); +cornerstoneTools.targetCRTouch = targetCRInterface.touch; + +var targetUNInterface = createQualitativeTargetTool('targetUN'); +cornerstoneTools.targetUN = targetUNInterface.mouse; +cornerstoneTools.targetUN.setConfiguration(configuration); +cornerstoneTools.targetUNTouch = targetUNInterface.touch; + +var targetEXInterface = createQualitativeTargetTool('targetEX'); +cornerstoneTools.targetEX = targetEXInterface.mouse; +cornerstoneTools.targetEX.setConfiguration(configuration); +cornerstoneTools.targetEXTouch = targetEXInterface.touch; diff --git a/Packages/ohif-lesiontracker/client/lib/studylist/studylistModification.js b/Packages/ohif-lesiontracker/client/lib/studylist/studylistModification.js index 5ecfbd952..9f4c8d55d 100644 --- a/Packages/ohif-lesiontracker/client/lib/studylist/studylistModification.js +++ b/Packages/ohif-lesiontracker/client/lib/studylist/studylistModification.js @@ -7,7 +7,6 @@ Meteor.startup(function() { OHIF.studylist.callbacks.middleClickOnStudy = dblClickOnStudy; OHIF.studylist.timepointApi = new OHIF.measurements.TimepointApi(); - OHIF.studylist.timepointApi.retrieveTimepoints({}); }); /** diff --git a/Packages/ohif-lesiontracker/server/methods.js b/Packages/ohif-lesiontracker/server/methods.js index cbb4db934..b6629b799 100644 --- a/Packages/ohif-lesiontracker/server/methods.js +++ b/Packages/ohif-lesiontracker/server/methods.js @@ -13,8 +13,13 @@ const Timepoints = new Mongo.Collection('timepoints'); Timepoints._debugName = 'Timepoints'; Timepoints.find().observe({ - remove() { - console.warn('REMOVED'); + added(document) { + console.warn('TIMEPOINT ADDED') + console.warn(document); + }, + remove(docId) { + console.warn('TIMEPOINT REMOVED'); + console.warn(docId); } }); diff --git a/Packages/ohif-log/main.js b/Packages/ohif-log/main.js index 1c42f9639..beab00033 100644 --- a/Packages/ohif-log/main.js +++ b/Packages/ohif-log/main.js @@ -1,7 +1,8 @@ import { OHIF } from 'meteor/ohif:core'; -import { loglevel } from 'meteor/practicalmeteor:loglevel'; +import loglevel from 'loglevel'; -const defaultLevel = Meteor.isProduction ? 'error' : 'trace'; +const defaultLevel = Meteor.isProduction ? 'ERROR' : 'TRACE'; // Create package logger using loglevel -OHIF.log = loglevel.createLogger('', defaultLevel); +OHIF.log = loglevel.getLogger('OHIF'); +OHIF.log.setLevel(defaultLevel); diff --git a/Packages/ohif-log/package.js b/Packages/ohif-log/package.js index d24b70200..8c319c55b 100644 --- a/Packages/ohif-log/package.js +++ b/Packages/ohif-log/package.js @@ -4,15 +4,16 @@ Package.describe({ version: '0.0.1' }); +Npm.depends({ + loglevel: '1.4.1' +}); + Package.onUse(function(api) { api.versionsFrom('1.4'); api.use('ecmascript'); api.use('standard-app-packages'); - // Control over logging - api.use('practicalmeteor:loglevel'); - // Our custom packages api.use('ohif:core'); diff --git a/Packages/ohif-measurements/both/configuration/timepoints.js b/Packages/ohif-measurements/both/configuration/timepoints.js index b96bbadc6..caa18e9f8 100644 --- a/Packages/ohif-measurements/both/configuration/timepoints.js +++ b/Packages/ohif-measurements/both/configuration/timepoints.js @@ -5,7 +5,7 @@ import { OHIF } from 'meteor/ohif:core'; import { schema as TimepointSchema } from 'meteor/ohif:measurements/both/schema/timepoints'; -let configuration = {}; +const configuration = {}; class TimepointApi { static setConfiguration(config) { @@ -29,19 +29,30 @@ class TimepointApi { retrieveTimepoints(filter) { const retrievalFn = configuration.dataExchange.retrieve; if (!_.isFunction(retrievalFn)) { + OHIF.log.error('Timepoint retrieval function has not been configured.') return; } return new Promise((resolve, reject) => { retrievalFn(filter).then(timepointData => { OHIF.log.info('Timepoint data retrieval'); - OHIF.log.info(timepointData); + _.each(timepointData, timepoint => { delete timepoint._id; - this.timepoints.insert(timepoint); + const query = { + timepointId: timepoint.timepointId + }; + + this.timepoints.update(query, { + $set: timepoint + }, { + upsert: true + }); }); resolve(); + }).catch(reason => { + OHIF.log.error(`Timepoint retrieval function failed: ${reason}`); }); }); } diff --git a/Packages/ohif-measurements/client/components/association/associationModal/studyAssociationTable/studyAssociationTable.styl b/Packages/ohif-measurements/client/components/association/associationModal/studyAssociationTable/studyAssociationTable.styl index cd074b85a..84bdebafa 100644 --- a/Packages/ohif-measurements/client/components/association/associationModal/studyAssociationTable/studyAssociationTable.styl +++ b/Packages/ohif-measurements/client/components/association/associationModal/studyAssociationTable/studyAssociationTable.styl @@ -65,6 +65,7 @@ $bodyCellHeight = 30px &.timepointOptions label font-weight: 200 + cursor: pointer input margin: 0 3px diff --git a/Packages/ohif-measurements/client/components/caseProgress/caseProgress.js b/Packages/ohif-measurements/client/components/caseProgress/caseProgress.js index 9c08bf0b8..972a70b64 100644 --- a/Packages/ohif-measurements/client/components/caseProgress/caseProgress.js +++ b/Packages/ohif-measurements/client/components/caseProgress/caseProgress.js @@ -27,7 +27,7 @@ Template.caseProgress.onCreated(() => { promise.then(successHandler).catch(errorHandler); OHIF.ui.showDialog('dialogLoading', { promise, - text: 'Saving measurements data' + text: 'Saving measurement data' }); return promise; diff --git a/Packages/ohif-measurements/client/lib/MeasurementHandlers.js b/Packages/ohif-measurements/client/lib/MeasurementHandlers.js index 7d8a1b080..e783fab06 100644 --- a/Packages/ohif-measurements/client/lib/MeasurementHandlers.js +++ b/Packages/ohif-measurements/client/lib/MeasurementHandlers.js @@ -107,6 +107,7 @@ class MeasurementHandlers { Collection._c2._simpleSchema.clean(measurement); // Insert the new measurement into the collection + console.log(measurement); Collection.update(measurementId, { $set: measurement }); diff --git a/Packages/ohif-measurements/package.js b/Packages/ohif-measurements/package.js index 7f1757681..9a8720f1e 100644 --- a/Packages/ohif-measurements/package.js +++ b/Packages/ohif-measurements/package.js @@ -1,6 +1,6 @@ Npm.depends({ ajv: '4.10.4', - jspdf: '1.3.2' + jspdf: '1.3.3' }); Package.describe({ diff --git a/Packages/ohif-study-list/client/components/studylist/studylistResult/studylistResult.js b/Packages/ohif-study-list/client/components/studylist/studylistResult/studylistResult.js index 990802fed..0cb388b3f 100644 --- a/Packages/ohif-study-list/client/components/studylist/studylistResult/studylistResult.js +++ b/Packages/ohif-study-list/client/components/studylist/studylistResult/studylistResult.js @@ -138,8 +138,8 @@ function search() { patientId: getFilter($('input#patientId').val()), accessionNumber: getFilter($('input#accessionNumber').val()), studyDescription: getFilter($('input#studyDescription').val()), - studyDateFrom: studyDateFrom, - studyDateTo: studyDateTo, + studyDateFrom, + studyDateTo, modalitiesInStudy: $('input#modality').val() ? $('input#modality').val() : '' }; @@ -161,6 +161,7 @@ function search() { Session.set('showLoadingText', false); if (!studies) { + OHIF.log.warn('No studies found'); return; } diff --git a/Packages/ohif-study-list/client/components/studylist/studylistStudy/studylistStudy.js b/Packages/ohif-study-list/client/components/studylist/studylistStudy/studylistStudy.js index fd8d70543..c470cfec2 100644 --- a/Packages/ohif-study-list/client/components/studylist/studylistStudy/studylistStudy.js +++ b/Packages/ohif-study-list/client/components/studylist/studylistStudy/studylistStudy.js @@ -1,6 +1,6 @@ import { Template } from 'meteor/templating'; import { Blaze } from 'meteor/blaze'; -import * as Hammer from 'hammerjs'; +import Hammer from 'hammerjs'; import { OHIF } from 'meteor/ohif:core'; // Clear all selected studies diff --git a/Packages/ohif-study-list/client/lib/retrieveStudyMetadata.js b/Packages/ohif-study-list/client/lib/retrieveStudyMetadata.js index 342439081..436e31e5d 100644 --- a/Packages/ohif-study-list/client/lib/retrieveStudyMetadata.js +++ b/Packages/ohif-study-list/client/lib/retrieveStudyMetadata.js @@ -33,6 +33,17 @@ OHIF.studylist.retrieveStudyMetadata = studyInstanceUid => { Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) { console.timeEnd('retrieveStudyMetadata'); + if (error) { + OHIF.log.error(error); + reject(error); + return; + } + + if (!study) { + reject(`GetStudyMetadata: No study data returned from server: ${studyInstanceUid}`); + return; + } + if (window.HipaaLogger && Meteor.user && Meteor.user()) { window.HipaaLogger.logEvent({ eventType: 'viewed', @@ -45,16 +56,6 @@ OHIF.studylist.retrieveStudyMetadata = studyInstanceUid => { }); } - if (error) { - OHIF.log.warn(error); - reject(error); - return; - } - - if (!study) { - throw new Meteor.Error('GetStudyMetadata', 'No study data returned from server'); - } - // Once the data was retrieved, the series are sorted by series and instance number OHIF.viewerbase.sortStudy(study); diff --git a/Packages/ohif-study-list/package.js b/Packages/ohif-study-list/package.js index fb4d3a6e0..0770d252f 100644 --- a/Packages/ohif-study-list/package.js +++ b/Packages/ohif-study-list/package.js @@ -5,7 +5,7 @@ Package.describe({ }); Npm.depends({ - hammerjs: '2.0.6' + hammerjs: '2.0.8' }); Package.onUse(function(api) { diff --git a/Packages/ohif-viewerbase/client/components/basic/errorText/errorText.html b/Packages/ohif-viewerbase/client/components/basic/errorText/errorText.html new file mode 100644 index 000000000..449ef9ce1 --- /dev/null +++ b/Packages/ohif-viewerbase/client/components/basic/errorText/errorText.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/Packages/ohif-viewerbase/client/components/basic/errorText/errorText.styl b/Packages/ohif-viewerbase/client/components/basic/errorText/errorText.styl new file mode 100644 index 000000000..6654d34be --- /dev/null +++ b/Packages/ohif-viewerbase/client/components/basic/errorText/errorText.styl @@ -0,0 +1,4 @@ +.errorTextDiv + font-weight: 300 + padding: 30px + text-align: center diff --git a/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js b/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js index e731e5dac..8e2348c07 100644 --- a/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js +++ b/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js @@ -35,6 +35,7 @@ Template.imageThumbnail.onRendered(() => { const element = $element.get(0); instance.refreshImage = () => { + console.log('imageThumbnail refreshImage'); if (!element) { return; } @@ -84,16 +85,14 @@ Template.imageThumbnail.onRendered(() => { }); Template.imageThumbnail.onDestroyed(() => { + console.log('imagethumbnail onDestroyed'); const instance = Template.instance(); // Declare DOM and jQuery objects const $parent = instance.$('.imageThumbnail'); const $element = $parent.find('.imageThumbnailCanvas'); const element = $element.get(0); - - if (element) { - cornerstone.disable(element); - } + cornerstone.disable(element); }); Template.imageThumbnail.helpers({ diff --git a/Packages/ohif-viewerbase/client/lib/StackManager.js b/Packages/ohif-viewerbase/client/lib/StackManager.js index c446dcca0..c9e7a6fc6 100644 --- a/Packages/ohif-viewerbase/client/lib/StackManager.js +++ b/Packages/ohif-viewerbase/client/lib/StackManager.js @@ -26,8 +26,8 @@ function createAndAddStack(stackMap, study, displaySet) { const metaData = { instance: image, // in this context, instance will be the data of the InstanceMetadata object... series: displaySet, // TODO: Check this - study: study, - numImages: numImages, + study, + numImages, imageIndex: imageIndex + 1 }; @@ -40,8 +40,7 @@ function createAndAddStack(stackMap, study, displaySet) { imageIds.push(imageId); metadataProvider.addMetadata(imageId, metaData); } - } - else { + } else { imageId = getImageId(image); imageIds.push(imageId); metadataProvider.addMetadata(imageId, metaData); @@ -50,7 +49,7 @@ function createAndAddStack(stackMap, study, displaySet) { const stack = { displaySetInstanceUid: displaySet.displaySetInstanceUid, - imageIds: imageIds, + imageIds, frameRate: displaySet.frameRate, isClip: displaySet.isClip }; @@ -61,7 +60,7 @@ function createAndAddStack(stackMap, study, displaySet) { } configuration = { - createAndAddStack: createAndAddStack + createAndAddStack }; /** diff --git a/Packages/ohif-viewerbase/client/lib/classes/ResizeViewportManager.js b/Packages/ohif-viewerbase/client/lib/classes/ResizeViewportManager.js index 3d10ddcf9..8f780c6bc 100644 --- a/Packages/ohif-viewerbase/client/lib/classes/ResizeViewportManager.js +++ b/Packages/ohif-viewerbase/client/lib/classes/ResizeViewportManager.js @@ -7,7 +7,6 @@ import { getInstanceClassDefaultViewport } from '../instanceClassSpecificViewpor export class ResizeViewportManager { constructor() { this._resizeHandler = null; - OHIF.log.info('ResizeViewportManager'); } // Reposition Study Series Quick Switch based whether side bars are opened or not diff --git a/Packages/ohif-viewerbase/client/lib/classes/StudyLoadingListener.js b/Packages/ohif-viewerbase/client/lib/classes/StudyLoadingListener.js index 267ef6af3..c0ab686f9 100644 --- a/Packages/ohif-viewerbase/client/lib/classes/StudyLoadingListener.js +++ b/Packages/ohif-viewerbase/client/lib/classes/StudyLoadingListener.js @@ -118,12 +118,12 @@ class DICOMFileLoadingListener extends BaseLoadingListener { this.stopListening(); - $(cornerstone).on(imageLoadProgressEventName, imageLoadProgressEventHandle); + $(cornerstone.events).on(imageLoadProgressEventName, imageLoadProgressEventHandle); } stopListening() { const imageLoadProgressEventName = this._getImageLoadProgressEventName(); - $(cornerstone).off(imageLoadProgressEventName); + $(cornerstone.events).off(imageLoadProgressEventName); } _imageLoadProgressEventHandle(e, eventData) { @@ -241,16 +241,16 @@ class StackLoadingListener extends BaseLoadingListener { this.stopListening(); - $(cornerstone).on(imageLoadedEventName, imageLoadedEventHandle); - $(cornerstone).on(imageCachePromiseRemovedEventName, imageCachePromiseRemovedEventHandle); + $(cornerstone.events).on(imageLoadedEventName, imageLoadedEventHandle); + $(cornerstone.events).on(imageCachePromiseRemovedEventName, imageCachePromiseRemovedEventHandle); } stopListening() { const imageLoadedEventName = this._getImageLoadedEventName(); const imageCachePromiseRemovedEventName = this._getImageCachePromiseRemoveEventName(); - $(cornerstone).off(imageLoadedEventName); - $(cornerstone).off(imageCachePromiseRemovedEventName); + $(cornerstone.events).off(imageLoadedEventName); + $(cornerstone.events).off(imageCachePromiseRemovedEventName); } _updateFrameStatus(imageId, loaded) { diff --git a/Packages/ohif-viewerbase/client/lib/classes/StudyPrefetcher.js b/Packages/ohif-viewerbase/client/lib/classes/StudyPrefetcher.js index 6b12fba3c..9537462f1 100644 --- a/Packages/ohif-viewerbase/client/lib/classes/StudyPrefetcher.js +++ b/Packages/ohif-viewerbase/client/lib/classes/StudyPrefetcher.js @@ -13,12 +13,12 @@ export class StudyPrefetcher { this.prefetchDisplaySetsTimeout = 300; this.lastActiveViewportElement = null; - $(cornerstone).on('CornerstoneImageCacheFull.StudyPrefetcher', _.bind(this.cacheFullHandler, this)); + $(cornerstone.events).on('CornerstoneImageCacheFull.StudyPrefetcher', _.bind(this.cacheFullHandler, this)); } destroy() { this.stopPrefetching(); - $(cornerstone).off('CornerstoneImageCacheFull.StudyPrefetcher'); + $(cornerstone.events).off('CornerstoneImageCacheFull.StudyPrefetcher'); } static getInstance() { @@ -162,7 +162,7 @@ export class StudyPrefetcher { getStudy(image) { const studyMetadata = cornerstoneTools.metaData.get('study', image.imageId); - return _.find(this.studies, study => study.studyInstanceUid === studyMetadata.studyInstanceUid); + return OHIF.viewer.Studies.find(study => study.studyInstanceUid === studyMetadata.studyInstanceUid); } getSeries(study, image) { @@ -191,6 +191,10 @@ export class StudyPrefetcher { } const study = this.getStudy(image); + if (!study.seriesList || !study.seriesList.length) { + console.error(study); + } + const series = this.getSeries(study, image); const instance = this.getInstance(series, image); const displaySets = study.displaySets; diff --git a/Packages/ohif-viewerbase/client/lib/renderViewer.js b/Packages/ohif-viewerbase/client/lib/renderViewer.js index 7e3248a9d..7276e46ae 100644 --- a/Packages/ohif-viewerbase/client/lib/renderViewer.js +++ b/Packages/ohif-viewerbase/client/lib/renderViewer.js @@ -22,5 +22,12 @@ export const renderViewer = (context, params, layoutTemplate='app') => { studies } }); + }).catch(error => { + context.render(layoutTemplate, { + data: { + template: 'errorText', + error + } + }); }); }; diff --git a/Packages/ohif-viewerbase/client/lib/toolManager.js b/Packages/ohif-viewerbase/client/lib/toolManager.js index ffb9acfc8..eeb5ad0fd 100644 --- a/Packages/ohif-viewerbase/client/lib/toolManager.js +++ b/Packages/ohif-viewerbase/client/lib/toolManager.js @@ -235,8 +235,8 @@ export const toolManager = { // Whenever the CornerstoneImageLoadProgress is fired, identify which viewports // the "in-progress" image is to be displayed in. Then pass the percent complete // via the Meteor Session to the other templates to be displayed in the relevant viewports. - $(cornerstone).on('CornerstoneImageLoadProgress', (e, eventData) => { - const viewportIndices = this.getKeysByValue(window.ViewportLoading, eventData.imageId); + $(cornerstone.events).on('CornerstoneImageLoadProgress', (e, eventData) => { + viewportIndices = this.getKeysByValue(window.ViewportLoading, eventData.imageId); viewportIndices.forEach(viewportIndex => { Session.set('CornerstoneLoadProgress' + viewportIndex, eventData.percentComplete); }); diff --git a/Packages/ohif-viewerbase/package.js b/Packages/ohif-viewerbase/package.js index f594ab7e4..695c3b6d9 100644 --- a/Packages/ohif-viewerbase/package.js +++ b/Packages/ohif-viewerbase/package.js @@ -95,6 +95,8 @@ Package.onUse(function(api) { api.addFiles('client/components/basic/layout/layout.styl', 'client'); api.addFiles('client/components/basic/loadingText/loadingText.html', 'client'); api.addFiles('client/components/basic/loadingText/loadingText.styl', 'client'); + api.addFiles('client/components/basic/errorText/errorText.html', 'client'); + api.addFiles('client/components/basic/errorText/errorText.styl', 'client'); api.addFiles('client/components/basic/removableBackdrop/removableBackdrop.html', 'client'); api.addFiles('client/components/basic/removableBackdrop/removableBackdrop.styl', 'client'); diff --git a/StandaloneViewer/StandaloneViewer/.meteor/versions b/StandaloneViewer/StandaloneViewer/.meteor/versions index 0fda50604..491fba7eb 100644 --- a/StandaloneViewer/StandaloneViewer/.meteor/versions +++ b/StandaloneViewer/StandaloneViewer/.meteor/versions @@ -90,8 +90,6 @@ ohif:viewerbase@0.0.1 ohif:wadoproxy@0.0.1 ordered-dict@1.0.9 peppelg:bootstrap-3-modal@1.0.4 -practicalmeteor:chai@2.1.0_1 -practicalmeteor:loglevel@1.2.0_2 promise@0.8.8 raix:eventemitter@0.1.3 random@1.0.10 diff --git a/StandaloneViewer/StandaloneViewer/client/cornerstoneWebImageLoader.js b/StandaloneViewer/StandaloneViewer/client/cornerstoneWebImageLoader.js index 079b117ee..fb5f3eb85 100644 --- a/StandaloneViewer/StandaloneViewer/client/cornerstoneWebImageLoader.js +++ b/StandaloneViewer/StandaloneViewer/client/cornerstoneWebImageLoader.js @@ -1,164 +1,362 @@ -/*! cornerstoneWebImageLoader - v0.6.0 - 2016-02-09 | (c) 2015 Chris Hafey | https://github.com/chafey/cornerstoneWebImageLoader */ -cornerstoneWebImageLoader = {}; +/*! cornerstone-web-image-loader - 0.8.1 - 2017-06-08 | (c) 2016 Chris Hafey | https://github.com/chafey/cornerstoneWebImageLoader */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(require("cornerstone-core")); + else if(typeof define === 'function' && define.amd) + define("cornerstoneWebImageLoader", ["cornerstone-core"], factory); + else if(typeof exports === 'object') + exports["cornerstoneWebImageLoader"] = factory(require("cornerstone-core")); + else + root["cornerstoneWebImageLoader"] = factory(root["cornerstone"]); +})(this, function(__WEBPACK_EXTERNAL_MODULE_0__) { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 4); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +module.exports = __WEBPACK_EXTERNAL_MODULE_0__; + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (arrayBuffer) { + var deferred = $.Deferred(); + + var image = new Image(); + + var arrayBufferView = new Uint8Array(arrayBuffer); + var blob = new Blob([arrayBufferView]); + var urlCreator = window.URL || window.webkitURL; + var imageUrl = urlCreator.createObjectURL(blob); + + image.src = imageUrl; + image.onload = function () { + deferred.resolve(image); + urlCreator.revokeObjectURL(imageUrl); + }; + image.onerror = function (err) { + urlCreator.revokeObjectURL(imageUrl); + deferred.reject(err); + }; + + return deferred.promise(); +}; + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (image, imageId) { + // extract the attributes we need + var rows = image.naturalHeight; + var columns = image.naturalWidth; + + function getPixelData() { + var imageData = getImageData(); + var imageDataData = imageData.data; + var numPixels = image.naturalHeight * image.naturalWidth; + var storedPixelData = new Uint8Array(numPixels * 4); + var imageDataIndex = 0; + var storedPixelDataIndex = 0; + + for (var i = 0; i < numPixels; i++) { + storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; + storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; + storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; + storedPixelData[storedPixelDataIndex++] = 255; // alpha + imageDataIndex++; + } + + return storedPixelData; + } + + function getImageData() { + var context = void 0; + + if (lastImageIdDrawn === imageId) { + context = canvas.getContext('2d'); + } else { + canvas.height = image.naturalHeight; + canvas.width = image.naturalWidth; + context = canvas.getContext('2d'); + context.drawImage(image, 0, 0); + lastImageIdDrawn = imageId; + } + + return context.getImageData(0, 0, image.naturalWidth, image.naturalHeight); + } + + function getCanvas() { + if (lastImageIdDrawn === imageId) { + return canvas; + } + + canvas.height = image.naturalHeight; + canvas.width = image.naturalWidth; + var context = canvas.getContext('2d'); + + context.drawImage(image, 0, 0); + lastImageIdDrawn = imageId; + + return canvas; + } + + function getImage() { + return image; + } + + // Extract the various attributes we need + return { + imageId: imageId, + minPixelValue: 0, // calculated below + maxPixelValue: 255, // calculated below + slope: 1.0, + intercept: 0, + windowCenter: 128, + windowWidth: 255, + render: cornerstone.renderWebImage, + getPixelData: getPixelData, + getImageData: getImageData, + getCanvas: getCanvas, + getImage: getImage, + // storedPixelData: extractStoredPixels(image), + rows: rows, + columns: columns, + height: rows, + width: columns, + color: true, + columnPixelSpacing: undefined, + rowPixelSpacing: undefined, + invert: false, + sizeInBytes: rows * columns * 4 // we don't know for sure so we over estimate to be safe + }; +}; + +var _cornerstoneCore = __webpack_require__(0); + +var cornerstone = _interopRequireWildcard(_cornerstoneCore); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +var canvas = document.createElement('canvas'); +var lastImageIdDrawn = ''; + +/** + * creates a cornerstone Image object for the specified Image and imageId + * + * @param image - An Image + * @param imageId - the imageId for this image + * @returns Cornerstone Image Object + */ + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.loadImage = loadImage; +exports.configure = configure; + +var _cornerstoneCore = __webpack_require__(0); + +var cornerstone = _interopRequireWildcard(_cornerstoneCore); + +var _arrayBufferToImage = __webpack_require__(1); + +var _arrayBufferToImage2 = _interopRequireDefault(_arrayBufferToImage); + +var _createImage = __webpack_require__(2); + +var _createImage2 = _interopRequireDefault(_createImage); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + // // This is a cornerstone image loader for web images such as PNG and JPEG // +var options = { + // callback allowing customization of the xhr (e.g. adding custom auth headers, cors, etc) + beforeSend: function beforeSend() /* xhr */{} +}; -(function ($, cornerstone, cornerstoneWebImageLoader) { +// Loads an image given a url to an image +function loadImage(imageId) { + var deferred = $.Deferred(); - "use strict"; + var xhr = new XMLHttpRequest(); - var canvas = document.createElement('canvas'); - var lastImageIdDrawn = ""; + xhr.open('GET', imageId, true); + xhr.responseType = 'arraybuffer'; + options.beforeSend(xhr); + xhr.onload = function () { + var imagePromise = (0, _arrayBufferToImage2.default)(this.response); - var options = { - // callback allowing customization of the xhr (e.g. adding custom auth headers, cors, etc) - beforeSend : function(xhr) {} - }; + imagePromise.then(function (image) { + var imageObject = (0, _createImage2.default)(image, imageId); - function createImageObject(image, imageId) - { - // extract the attributes we need - var rows = image.naturalHeight; - var columns = image.naturalWidth; + deferred.resolve(imageObject); + }, function (error) { + deferred.reject(error); + }); + }; + xhr.onprogress = function (oProgress) { - function getPixelData() - { - var imageData = getImageData(); - var imageDataData = imageData.data; - var numPixels = image.naturalHeight * image.naturalWidth; - var storedPixelData = new Uint8Array(numPixels * 4); - var imageDataIndex = 0; - var storedPixelDataIndex = 0; - for(var i=0; i < numPixels; i++) { - storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; - storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; - storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; - storedPixelData[storedPixelDataIndex++] = 255; // alpha - imageDataIndex++; - } - return storedPixelData; - } + if (oProgress.lengthComputable) { + // evt.loaded the bytes browser receive + // evt.total the total bytes seted by the header + // + var loaded = oProgress.loaded; + var total = oProgress.total; + var percentComplete = Math.round(loaded / total * 100); - function getImageData() - { - var context; - if(lastImageIdDrawn !== imageId) { - canvas.height = image.naturalHeight; - canvas.width = image.naturalWidth; - context = canvas.getContext('2d'); - context.drawImage(image, 0, 0); - lastImageIdDrawn = imageId; - } - else { - context = canvas.getContext('2d'); - } - return context.getImageData(0, 0, image.naturalWidth, image.naturalHeight); - } - - function getCanvas() - { - if(lastImageIdDrawn === imageId) { - return canvas; - } - - canvas.height = image.naturalHeight; - canvas.width = image.naturalWidth; - var context = canvas.getContext('2d'); - context.drawImage(image, 0, 0); - lastImageIdDrawn = imageId; - return canvas; - } - - function getImage() - { - return image; - } - - // Extract the various attributes we need - return { - imageId : imageId, - minPixelValue : 0, // calculated below - maxPixelValue : 255, // calculated below - slope: 1.0, - intercept: 0, - windowCenter : 128, - windowWidth : 255, - render: cornerstone.renderColorImage, - getPixelData: getPixelData, - getImageData: getImageData, - getCanvas: getCanvas, - getImage: getImage, - //storedPixelData: extractStoredPixels(image), - rows: rows, - columns: columns, - height: rows, - width: columns, - color: true, - columnPixelSpacing: undefined, - rowPixelSpacing: undefined, - invert: false, - sizeInBytes : rows * columns * 4 // we don't know for sure so we over estimate to be safe - }; + $(cornerstone.events).trigger('CornerstoneImageLoadProgress', { + imageId: imageId, + loaded: loaded, + total: total, + percentComplete: percentComplete + }); } + }; + xhr.send(); - // Loads an image given a url to an image - function loadImage(imageId) { + return deferred.promise(); +} - // create a deferred object - var deferred = $.Deferred(); +function configure(opts) { + options = opts; +} - var image = new Image(); +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { - var xhr = new XMLHttpRequest(); - xhr.open("GET", imageId, true); - xhr.responseType = "arraybuffer"; - options.beforeSend(xhr); - xhr.onload = function() { - var arrayBufferView = new Uint8Array(this.response); - var blob = new Blob([arrayBufferView], {type: "image/jpeg"}); - var urlCreator = window.URL || window.webkitURL; - var imageUrl = urlCreator.createObjectURL(blob); - image.src = imageUrl; - image.onload = function() { - var imageObject = createImageObject(image, imageId); - deferred.resolve(imageObject); - urlCreator.revokeObjectURL(imageUrl); - }; - image.onerror = function() { - urlCreator.revokeObjectURL(imageUrl); - deferred.reject(); - }; - }; - xhr.onprogress = function(oProgress) { +"use strict"; - if (oProgress.lengthComputable) { //evt.loaded the bytes browser receive - //evt.total the total bytes seted by the header - // - var loaded = oProgress.loaded; - var total = oProgress.total; - var percentComplete = Math.round((loaded / total)*100); - $(cornerstone).trigger('CornerstoneImageLoadProgress', { - imageId: imageId, - loaded: loaded, - total: total, - percentComplete: percentComplete - }); - } - }; - xhr.send(); - return deferred.promise(); - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.configure = exports.loadImage = exports.createImage = exports.arrayBufferToImage = undefined; - function configure(opts) { - options = opts; - } +var _arrayBufferToImage = __webpack_require__(1); - // steam the http and https prefixes so we can use standard web urls directly - cornerstone.registerImageLoader('http', loadImage); - cornerstone.registerImageLoader('https', loadImage); - cornerstoneWebImageLoader.configure = configure; - return cornerstone; -}($, cornerstone, cornerstoneWebImageLoader)); +var _arrayBufferToImage2 = _interopRequireDefault(_arrayBufferToImage); + +var _createImage = __webpack_require__(2); + +var _createImage2 = _interopRequireDefault(_createImage); + +var _loadImage = __webpack_require__(3); + +var _cornerstoneCore = __webpack_require__(0); + +var cornerstone = _interopRequireWildcard(_cornerstoneCore); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.arrayBufferToImage = _arrayBufferToImage2.default; +exports.createImage = _createImage2.default; +exports.loadImage = _loadImage.loadImage; +exports.configure = _loadImage.configure; + +// Register the http and https prefixes so we can use standard web urls directly + +cornerstone.registerImageLoader('http', _loadImage.loadImage); +cornerstone.registerImageLoader('https', _loadImage.loadImage); + +/***/ }) +/******/ ]); +}); +//# sourceMappingURL=cornerstoneWebImageLoader.js.map \ No newline at end of file