@ohif/mode-tmtv 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/dist/ohif-mode-tmtv.umd.js +2 -0
- package/dist/ohif-mode-tmtv.umd.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Open Health Imaging Foundation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Total Metabolic Tumor Volume
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Total Metabolic Tumor Volume (TMTV) workflow mode enables quantitatively measurement of a tumor volume in a patient's body.
|
|
6
|
+
This mode is accessible in any study that has a PT and CT image series as you can see below
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
Note: If the study does not have a PT and CT image series, the TMTV workflow mode will not be available
|
|
12
|
+
and will become grayed out.
|
|
13
|
+
|
|
14
|
+
## Layout
|
|
15
|
+
The designed layout for the viewports follows a predefined hanging protocol which will place
|
|
16
|
+
10 viewports containing CT, PT, Fusion and Maximum Intensity Projection (MIP) PT scenes.
|
|
17
|
+
|
|
18
|
+
The hanging protocol will match the CT and PT displaySets based on series description. In terms
|
|
19
|
+
of PT displaySets, the hanging protocol will match the PT displaySet that has attenuated
|
|
20
|
+
corrected PET image data.
|
|
21
|
+
|
|
22
|
+
As seen in the image below, the first row contains CT volume in 3 different views of Axial,
|
|
23
|
+
Sagittal and Coronal. The second row contains PT volume in the same views as the first row.
|
|
24
|
+
The last row contains the fusion volume and the viewport to the right is a MIP of the PT
|
|
25
|
+
Volume in the Sagittal view.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
## Synchronization
|
|
32
|
+
|
|
33
|
+
The viewports in the 3 rows are synchronized both for the Camera and WindowLevel.
|
|
34
|
+
It means that when you interact with the CT viewport (pan, zoom, scroll),
|
|
35
|
+
the PT and Fusion viewports will be synchronized to the same view. In addition
|
|
36
|
+
to camera synchronization, the window level of the CT viewport will be synchronized
|
|
37
|
+
with the fusion viewport.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### MIP
|
|
41
|
+
The tools that are activated on each viewport is unique to its data. For instance,
|
|
42
|
+
the mouse scroll tool for PT, CT and Fusion viewports are scrolling through the image data
|
|
43
|
+
(in different directions); however, the mouse scroll tool for the MIP viewport will
|
|
44
|
+
rotate the camera to match the usecase for the MIP.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## Panels
|
|
48
|
+
There are two panels that are available in the TMTV workflow mode and we will
|
|
49
|
+
discuss them in detail below.
|
|
50
|
+
|
|
51
|
+
### SUV Panel
|
|
52
|
+
This panel shows the PT metadata derived from the matched PT displaySet. The user
|
|
53
|
+
can edit/change the metadata if needed, and by reloading the data the new
|
|
54
|
+
metadata will be applied to the PT volume.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## ROI Threshold Panel
|
|
58
|
+
The ROI Threshold panel is a panel that allows the user to use the `RectangleROIStartEnd`
|
|
59
|
+
tool from Cornerstone to define and edit a region of interest. Then, the user can
|
|
60
|
+
apply a threshold to the pixels in the ROI and save the result as a segmentation volume.
|
|
61
|
+
|
|
62
|
+
By applying each threshold to the ROI, the Total Metabolic Tumor Volume (TMTV), and
|
|
63
|
+
the SUV Peak values will get calculated for the labelmap segments and shown in the
|
|
64
|
+
panel.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Export Report
|
|
68
|
+
|
|
69
|
+
Finally, the results can be saved in the CSV format. The RectangleROI annotations
|
|
70
|
+
can also be extracted as a dicom RT Structure Set and saved as a DICOM file.
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
## Video Tutorial
|
|
74
|
+
|
|
75
|
+
Below you can see a video tutorial on how to use the TMTV workflow mode.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
https://user-images.githubusercontent.com/7490180/171065443-35369fba-e955-48ac-94da-d262e0fccb6b.mp4
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(o,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("@ohif/core"),require("@ohif/ui")):"function"==typeof define&&define.amd?define(["@ohif/core","@ohif/ui"],e):"object"==typeof exports?exports["ohif-mode-tmtv"]=e(require("@ohif/core"),require("@ohif/ui")):o["ohif-mode-tmtv"]=e(o["@ohif/core"],o["@ohif/ui"])}(globalThis,((o,e)=>(()=>{"use strict";var t={762:e=>{e.exports=o},369:o=>{o.exports=e}},n={};function i(o){var e=n[o];if(void 0!==e)return e.exports;var s=n[o]={exports:{}};return t[o](s,s.exports,i),s.exports}i.d=(o,e)=>{for(var t in e)i.o(e,t)&&!i.o(o,t)&&Object.defineProperty(o,t,{enumerable:!0,get:e[t]})},i.o=(o,e)=>Object.prototype.hasOwnProperty.call(o,e);var s={};return(()=>{i.d(s,{default:()=>A});var o=i(762),e=i(369);const t={CT:"ctToolGroup",PT:"ptToolGroup",Fusion:"fusionToolGroup",MIP:"mipToolGroup",default:"default"};const n=function(o,e,n,i){!function(o,e,n,i){const s={active:[{toolName:o.WindowLevel,bindings:[{mouseButton:e.MouseBindings.Primary}]},{toolName:o.Pan,bindings:[{mouseButton:e.MouseBindings.Auxiliary}]},{toolName:o.Zoom,bindings:[{mouseButton:e.MouseBindings.Secondary}]},{toolName:o.StackScrollMouseWheel,bindings:[]}],passive:[{toolName:o.Length},{toolName:o.ArrowAnnotate},{toolName:o.Bidirectional},{toolName:o.DragProbe},{toolName:o.Probe},{toolName:o.EllipticalROI},{toolName:o.RectangleROI},{toolName:o.StackScroll},{toolName:o.Angle},{toolName:o.CobbAngle},{toolName:o.Magnify}],enabled:[{toolName:o.SegmentationDisplay}],disabled:[{toolName:o.Crosshairs}]},l={[o.Crosshairs]:{viewportIndicators:!1,autoPan:{enabled:!1,panSize:10}},[o.ArrowAnnotate]:{getTextCallback:(o,e)=>{i.runCommand("arrowTextCallback",{callback:o,eventDetails:e})},changeTextCallback:(o,e,t)=>i.runCommand("arrowTextCallback",{callback:t,data:o,eventDetails:e})}};n.createToolGroupAndAddTools(t.CT,s,l),n.createToolGroupAndAddTools(t.PT,{active:s.active,passive:[...s.passive,{toolName:"RectangleROIStartEndThreshold"}],enabled:s.enabled,disabled:s.disabled},l),n.createToolGroupAndAddTools(t.Fusion,s,l),n.createToolGroupAndAddTools(t.default,s,l);const a={active:[{toolName:o.VolumeRotateMouseWheel},{toolName:o.MipJumpToClick,bindings:[{mouseButton:e.MouseBindings.Primary}]}],enabled:[{toolName:o.SegmentationDisplay}]},r={[o.VolumeRotateMouseWheel]:{rotateIncrementDegrees:.1},[o.MipJumpToClick]:{targetViewportIds:["ptAXIAL","ptCORONAL","ptSAGITTAL"]}};n.createToolGroupAndAddTools(t.MIP,a,r)}(o,e,n,i)},{windowLevelPresets:l}=o.defaults;function a(o,e,t,n,i,s){return{id:e,icon:t,label:n,type:o,commands:i,tooltip:s}}function r(o,e){return{id:o.toString(),title:o,subtitle:o,type:"action",commands:[{commandName:"setFusionPTColormap",commandOptions:{toolGroupId:t.Fusion,colormap:e}},{commandName:"setFusionPTColormap",commandOptions:{toolGroupId:t.Fusion,colormap:e}}]}}a.bind(null,"action"),a.bind(null,"toggle");const d=a.bind(null,"tool");function c(o,e,t){return{id:o.toString(),title:e,subtitle:t,type:"action",commands:[{commandName:"setWindowLevel",commandOptions:{...l[o]},context:"CORNERSTONE"}]}}function u(o,e,t){return t.map((t=>({commandName:o,commandOptions:{toolName:e,toolGroupId:t},context:"CORNERSTONE"})))}const m=[{id:"MeasurementTools",type:"ohif.splitButton",props:{groupId:"MeasurementTools",isRadio:!0,primary:d("Length","tool-length","Length",[...u("setToolActive","Length",[t.CT,t.PT,t.Fusion])],"Length"),secondary:{icon:"chevron-down",label:"",isActive:!0,tooltip:"More Measure Tools"},items:[d("Length","tool-length","Length",[...u("setToolActive","Length",[t.CT,t.PT,t.Fusion])],"Length Tool"),d("Bidirectional","tool-bidirectional","Bidirectional",[...u("setToolActive","Bidirectional",[t.CT,t.PT,t.Fusion])],"Bidirectional Tool"),d("ArrowAnnotate","tool-annotate","Annotation",[...u("setToolActive","ArrowAnnotate",[t.CT,t.PT,t.Fusion])],"Arrow Annotate"),d("EllipticalROI","tool-elipse","Ellipse",[...u("setToolActive","EllipticalROI",[t.CT,t.PT,t.Fusion])],"Ellipse Tool"),d("CircleROI","tool-circle","Circle",[...u("setToolActive","CircleROI",[t.CT,t.PT,t.Fusion])],"Circle Tool")]}},{id:"Zoom",type:"ohif.radioGroup",props:{type:"tool",icon:"tool-zoom",label:"Zoom",commands:[...u("setToolActive","Zoom",[t.CT,t.PT,t.Fusion])]}},{id:"MPR",type:"ohif.action",props:{type:"toggle",icon:"icon-mpr",label:"MPR",commands:[{commandName:"toggleHangingProtocol",commandOptions:{protocolId:"mpr"},context:"DEFAULT"}]}},{id:"WindowLevel",type:"ohif.splitButton",props:{groupId:"WindowLevel",primary:d("WindowLevel","tool-window-level","Window Level",[...u("setToolActive","WindowLevel",[t.CT,t.PT,t.Fusion])],"Window Level"),secondary:{icon:"chevron-down",label:"W/L Manual",isActive:!0,tooltip:"W/L Presets"},isAction:!0,renderer:e.WindowLevelMenuItem,items:[c(1,"Soft tissue","400 / 40"),c(2,"Lung","1500 / -600"),c(3,"Liver","150 / 90"),c(4,"Bone","2500 / 480"),c(5,"Brain","80 / 40")]}},{id:"Crosshairs",type:"ohif.radioGroup",props:{type:"tool",icon:"tool-crosshair",label:"Crosshairs",commands:[...u("setToolActive","Crosshairs",[t.CT,t.PT,t.Fusion])]}},{id:"Pan",type:"ohif.radioGroup",props:{type:"tool",icon:"tool-move",label:"Pan",commands:[...u("setToolActive","Pan",[t.CT,t.PT,t.Fusion])]}},{id:"RectangleROIStartEndThreshold",type:"ohif.radioGroup",props:{type:"tool",icon:"tool-create-threshold",label:"Rectangle ROI Threshold",commands:[...u("setToolActive","RectangleROIStartEndThreshold",[t.PT]),{commandName:"displayNotification",commandOptions:{title:"RectangleROI Threshold Tip",text:"RectangleROI Threshold tool should be used on PT Axial Viewport",type:"info"}},{commandName:"setViewportActive",commandOptions:{viewportId:"ptAXIAL"}}]}},{id:"fusionPTColormap",type:"ohif.splitButton",props:{groupId:"fusionPTColormap",primary:d("fusionPTColormap","tool-fusion-color","Fusion PT Colormap",[],"Fusion PT Colormap"),secondary:{icon:"chevron-down",label:"PT Colormap",isActive:!0,tooltip:"PET Image Colormap"},isAction:!0,renderer:e.WindowLevelMenuItem,items:[r("HSV","hsv"),r("Hot Iron","hot_iron"),r("S PET","s_pet"),r("Red Hot","red_hot"),r("Perfusion","perfusion"),r("Rainbow","rainbow_2"),r("SUV","suv"),r("GE 256","ge_256"),r("GE","ge"),r("Siemens","siemens")]}}],p=JSON.parse('{"u2":"@ohif/mode-tmtv"}').u2;const{MetadataProvider:T}=o.classes,g="@ohif/extension-default.layoutTemplateModule.viewerLayout",h="@ohif/extension-default.sopClassHandlerModule.stack",v="@ohif/extension-cornerstone.viewportModule.cornerstone",f="@ohif/extension-tmtv.hangingProtocolModule.ptCT",y="@ohif/extension-tmtv.panelModule.petSUV",C="@ohif/extension-tmtv.panelModule.ROIThresholdSeg",I={"@ohif/extension-default":"^3.0.0","@ohif/extension-cornerstone":"^3.0.0","@ohif/extension-tmtv":"^3.0.0"};let b=[];const A={id:p,modeFactory:function(e){let{modeConfiguration:i}=e;return{id:p,routeName:"tmtv",displayName:"Total Metabolic Tumor Volume",onModeEnter:o=>{let{servicesManager:e,extensionManager:i,commandsManager:s}=o;const{toolbarService:l,toolGroupService:a,hangingProtocolService:r,displaySetService:d}=e.services,c=i.getModuleEntry("@ohif/extension-cornerstone.utilityModule.tools"),{toolNames:u,Enums:p}=c.exports;n(u,p,a,s);const{unsubscribe:g}=a.subscribe(a.EVENTS.VIEWPORT_ADDED,(()=>{const{displaySetMatchDetails:o}=r.getMatchDetails();!function(o,e,n,i){const s=o.get("ctDisplaySet");if(!s)return;const{SeriesInstanceUID:l}=s,a=i.getDisplaySetsForSeries(l),r={...n.getToolConfiguration(t.Fusion,e.Crosshairs),filterActorUIDsToSetSlabThickness:[a[0].displaySetInstanceUID]};n.setToolConfiguration(t.Fusion,e.Crosshairs,r)}(o,u,a,d),function(o,e,n,i){const s=o.get("ptDisplaySet");if(!s)return;const{SeriesInstanceUID:l}=s,a=i.getDisplaySetsForSeries(l);if(!a||0===a.length)return;const r=n.getToolConfiguration(t.Fusion,e.WindowLevel),d=n.getToolConfiguration(t.Fusion,e.EllipticalROI),c=`cornerstoneStreamingImageVolume:${a[0].displaySetInstanceUID}`,u={...r,volumeId:c},m={...d,volumeId:c};n.setToolConfiguration(t.Fusion,e.WindowLevel,u),n.setToolConfiguration(t.Fusion,e.EllipticalROI,m)}(o,u,a,d),l.recordInteraction({groupId:"WindowLevel",itemId:"WindowLevel",interactionType:"tool",commands:[{commandName:"setToolActive",commandOptions:{toolName:u.WindowLevel,toolGroupId:t.CT},context:"CORNERSTONE"},{commandName:"setToolActive",commandOptions:{toolName:u.WindowLevel,toolGroupId:t.PT},context:"CORNERSTONE"},{commandName:"setToolActive",commandOptions:{toolName:u.WindowLevel,toolGroupId:t.Fusion},context:"CORNERSTONE"}]})}));b.push(g),l.init(i),l.addButtons(m),l.createButtonSection("primary",["MeasurementTools","Zoom","WindowLevel","Crosshairs","Pan","RectangleROIStartEndThreshold","fusionPTColormap"]),r.addCustomAttribute("getPTVOIRange","get PT VOI based on corrected or not",(o=>{const e=o.find((o=>"PT"===o.Modality));if(!e)return;const{imageId:t}=e.images[0],n=T.get("scalingModule",t);return n&&n.suvbw?{windowWidth:5,windowCenter:2.5}:void 0}))},onModeExit:o=>{let{servicesManager:e}=o;const{toolGroupService:t,syncGroupService:n,segmentationService:i,cornerstoneViewportService:s}=e.services;b.forEach((o=>o())),t.destroy(),n.destroy(),i.destroy(),s.destroy()},validationTags:{study:[],series:[]},isValidMode:o=>{let{modalities:e,study:t}=o;const n=e.split("\\");return n.includes("CT")&&n.includes("PT")&&!["SM"].some((o=>n.includes(o)))&&"1.3.6.1.4.1.12842.1.1.14.3.20220915.105557.468.2963630849"!==t.studyInstanceUid},routes:[{path:"tmtv",layoutTemplate:o=>{let{location:e,servicesManager:t}=o;return{id:g,props:{rightPanels:[C,y],viewports:[{namespace:v,displaySetsToDisplay:[h]}]}}}}],extensions:I,hangingProtocol:f,sopClassHandlers:[h],hotkeys:[...o.hotkeys.defaults.hotkeyBindings]}},extensionDependencies:I}})(),s=s.default})()));
|
|
2
|
+
//# sourceMappingURL=ohif-mode-tmtv.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dist/ohif-mode-tmtv.umd.js","mappings":"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","sources":["webpack://ohif-mode-tmtv/webpack/universalModuleDefinition","webpack://ohif-mode-tmtv/external umd \"@ohif/core\"","webpack://ohif-mode-tmtv/external umd \"@ohif/ui\"","webpack://ohif-mode-tmtv/webpack/bootstrap","webpack://ohif-mode-tmtv/webpack/runtime/define property getters","webpack://ohif-mode-tmtv/webpack/runtime/hasOwnProperty shorthand","webpack://ohif-mode-tmtv/./initToolGroups.js","webpack://ohif-mode-tmtv/./toolbarButtons.js","webpack://ohif-mode-tmtv/./id.js","webpack://ohif-mode-tmtv/./index.js","webpack://ohif-mode-tmtv/./utils/setCrosshairsConfiguration.js","webpack://ohif-mode-tmtv/./utils/setFusionActiveVolume.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"@ohif/core\"), require(\"@ohif/ui\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"@ohif/core\", \"@ohif/ui\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ohif-mode-tmtv\"] = factory(require(\"@ohif/core\"), require(\"@ohif/ui\"));\n\telse\n\t\troot[\"ohif-mode-tmtv\"] = factory(root[\"@ohif/core\"], root[\"@ohif/ui\"]);\n})(globalThis, (__WEBPACK_EXTERNAL_MODULE__762__, __WEBPACK_EXTERNAL_MODULE__369__) => {\nreturn ","module.exports = __WEBPACK_EXTERNAL_MODULE__762__;","module.exports = __WEBPACK_EXTERNAL_MODULE__369__;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","export const toolGroupIds = {\n CT: 'ctToolGroup',\n PT: 'ptToolGroup',\n Fusion: 'fusionToolGroup',\n MIP: 'mipToolGroup',\n default: 'default',\n // MPR: 'mpr',\n};\n\nfunction _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) {\n const tools = {\n active: [\n {\n toolName: toolNames.WindowLevel,\n bindings: [{ mouseButton: Enums.MouseBindings.Primary }],\n },\n {\n toolName: toolNames.Pan,\n bindings: [{ mouseButton: Enums.MouseBindings.Auxiliary }],\n },\n {\n toolName: toolNames.Zoom,\n bindings: [{ mouseButton: Enums.MouseBindings.Secondary }],\n },\n { toolName: toolNames.StackScrollMouseWheel, bindings: [] },\n ],\n passive: [\n { toolName: toolNames.Length },\n { toolName: toolNames.ArrowAnnotate },\n { toolName: toolNames.Bidirectional },\n { toolName: toolNames.DragProbe },\n { toolName: toolNames.Probe },\n { toolName: toolNames.EllipticalROI },\n { toolName: toolNames.RectangleROI },\n { toolName: toolNames.StackScroll },\n { toolName: toolNames.Angle },\n { toolName: toolNames.CobbAngle },\n { toolName: toolNames.Magnify },\n ],\n enabled: [{ toolName: toolNames.SegmentationDisplay }],\n disabled: [{ toolName: toolNames.Crosshairs }],\n };\n\n const toolsConfig = {\n [toolNames.Crosshairs]: {\n viewportIndicators: false,\n autoPan: {\n enabled: false,\n panSize: 10,\n },\n },\n [toolNames.ArrowAnnotate]: {\n getTextCallback: (callback, eventDetails) => {\n commandsManager.runCommand('arrowTextCallback', {\n callback,\n eventDetails,\n });\n },\n\n changeTextCallback: (data, eventDetails, callback) =>\n commandsManager.runCommand('arrowTextCallback', {\n callback,\n data,\n eventDetails,\n }),\n },\n };\n\n toolGroupService.createToolGroupAndAddTools(\n toolGroupIds.CT,\n tools,\n toolsConfig\n );\n toolGroupService.createToolGroupAndAddTools(\n toolGroupIds.PT,\n {\n active: tools.active,\n passive: [\n ...tools.passive,\n { toolName: 'RectangleROIStartEndThreshold' },\n ],\n enabled: tools.enabled,\n disabled: tools.disabled,\n },\n toolsConfig\n );\n toolGroupService.createToolGroupAndAddTools(\n toolGroupIds.Fusion,\n tools,\n toolsConfig\n );\n toolGroupService.createToolGroupAndAddTools(\n toolGroupIds.default,\n tools,\n toolsConfig\n );\n\n const mipTools = {\n active: [\n {\n toolName: toolNames.VolumeRotateMouseWheel,\n },\n {\n toolName: toolNames.MipJumpToClick,\n bindings: [{ mouseButton: Enums.MouseBindings.Primary }],\n },\n ],\n enabled: [{ toolName: toolNames.SegmentationDisplay }],\n };\n\n const mipToolsConfig = {\n [toolNames.VolumeRotateMouseWheel]: {\n rotateIncrementDegrees: 0.1,\n },\n [toolNames.MipJumpToClick]: {\n targetViewportIds: ['ptAXIAL', 'ptCORONAL', 'ptSAGITTAL'],\n },\n };\n\n toolGroupService.createToolGroupAndAddTools(\n toolGroupIds.MIP,\n mipTools,\n mipToolsConfig\n );\n}\n\nfunction initMPRToolGroup(toolNames, Enums, toolGroupService, commandsManager) {\n const tools = {\n active: [\n {\n toolName: toolNames.WindowLevel,\n bindings: [{ mouseButton: Enums.MouseBindings.Primary }],\n },\n {\n toolName: toolNames.Pan,\n bindings: [{ mouseButton: Enums.MouseBindings.Auxiliary }],\n },\n {\n toolName: toolNames.Zoom,\n bindings: [{ mouseButton: Enums.MouseBindings.Secondary }],\n },\n { toolName: toolNames.StackScrollMouseWheel, bindings: [] },\n ],\n passive: [\n { toolName: toolNames.Length },\n { toolName: toolNames.ArrowAnnotate },\n { toolName: toolNames.Bidirectional },\n { toolName: toolNames.DragProbe },\n { toolName: toolNames.EllipticalROI },\n { toolName: toolNames.RectangleROI },\n { toolName: toolNames.StackScroll },\n { toolName: toolNames.Angle },\n { toolName: toolNames.CobbAngle },\n { toolName: toolNames.SegmentationDisplay },\n ],\n disabled: [{ toolName: toolNames.Crosshairs }],\n\n // enabled\n // disabled\n };\n\n const toolsConfig = {\n [toolNames.Crosshairs]: {\n viewportIndicators: false,\n autoPan: {\n enabled: false,\n panSize: 10,\n },\n },\n [toolNames.ArrowAnnotate]: {\n getTextCallback: (callback, eventDetails) =>\n commandsManager.runCommand('arrowTextCallback', {\n callback,\n eventDetails,\n }),\n\n changeTextCallback: (data, eventDetails, callback) =>\n commandsManager.runCommand('arrowTextCallback', {\n callback,\n data,\n eventDetails,\n }),\n },\n };\n\n toolGroupService.createToolGroupAndAddTools('mpr', tools, toolsConfig);\n}\n\nfunction initToolGroups(toolNames, Enums, toolGroupService, commandsManager) {\n _initToolGroups(toolNames, Enums, toolGroupService, commandsManager);\n // initMPRToolGroup(toolNames, Enums, toolGroupService, commandsManager);\n}\n\nexport default initToolGroups;\n","// TODO: torn, can either bake this here; or have to create a whole new button type\n// Only ways that you can pass in a custom React component for render :l\nimport { WindowLevelMenuItem } from '@ohif/ui';\nimport { defaults } from '@ohif/core';\nimport { toolGroupIds } from './initToolGroups';\nconst { windowLevelPresets } = defaults;\n/**\n *\n * @param {*} type - 'tool' | 'action' | 'toggle'\n * @param {*} id\n * @param {*} icon\n * @param {*} label\n */\nfunction _createButton(type, id, icon, label, commands, tooltip) {\n return {\n id,\n icon,\n label,\n type,\n commands,\n tooltip,\n };\n}\n\nfunction _createColormap(label, colormap) {\n return {\n id: label.toString(),\n title: label,\n subtitle: label,\n type: 'action',\n commands: [\n {\n commandName: 'setFusionPTColormap',\n commandOptions: {\n toolGroupId: toolGroupIds.Fusion,\n colormap,\n },\n },\n {\n commandName: 'setFusionPTColormap',\n commandOptions: {\n toolGroupId: toolGroupIds.Fusion,\n colormap,\n },\n },\n ],\n };\n}\n\nconst _createActionButton = _createButton.bind(null, 'action');\nconst _createToggleButton = _createButton.bind(null, 'toggle');\nconst _createToolButton = _createButton.bind(null, 'tool');\n\n/**\n *\n * @param {*} preset - preset number (from above import)\n * @param {*} title\n * @param {*} subtitle\n */\nfunction _createWwwcPreset(preset, title, subtitle) {\n return {\n id: preset.toString(),\n title,\n subtitle,\n type: 'action',\n commands: [\n {\n commandName: 'setWindowLevel',\n commandOptions: {\n ...windowLevelPresets[preset],\n },\n context: 'CORNERSTONE',\n },\n ],\n };\n}\n\nfunction _createCommands(commandName, toolName, toolGroupIds) {\n return toolGroupIds.map(toolGroupId => ({\n /* It's a command that is being run when the button is clicked. */\n commandName,\n commandOptions: {\n toolName,\n toolGroupId,\n },\n context: 'CORNERSTONE',\n }));\n}\n\nconst toolbarButtons = [\n // Measurement\n {\n id: 'MeasurementTools',\n type: 'ohif.splitButton',\n props: {\n groupId: 'MeasurementTools',\n isRadio: true, // ?\n // Switch?\n primary: _createToolButton(\n 'Length',\n 'tool-length',\n 'Length',\n [\n ..._createCommands('setToolActive', 'Length', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n 'Length'\n ),\n secondary: {\n icon: 'chevron-down',\n label: '',\n isActive: true,\n tooltip: 'More Measure Tools',\n },\n items: [\n _createToolButton(\n 'Length',\n 'tool-length',\n 'Length',\n [\n ..._createCommands('setToolActive', 'Length', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n 'Length Tool'\n ),\n _createToolButton(\n 'Bidirectional',\n 'tool-bidirectional',\n 'Bidirectional',\n [\n ..._createCommands('setToolActive', 'Bidirectional', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n 'Bidirectional Tool'\n ),\n _createToolButton(\n 'ArrowAnnotate',\n 'tool-annotate',\n 'Annotation',\n [\n ..._createCommands('setToolActive', 'ArrowAnnotate', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n 'Arrow Annotate'\n ),\n _createToolButton(\n 'EllipticalROI',\n 'tool-elipse',\n 'Ellipse',\n [\n ..._createCommands('setToolActive', 'EllipticalROI', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n 'Ellipse Tool'\n ),\n _createToolButton(\n 'CircleROI',\n 'tool-circle',\n 'Circle',\n [\n ..._createCommands('setToolActive', 'CircleROI', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n 'Circle Tool'\n ),\n ],\n },\n },\n // Zoom..\n {\n id: 'Zoom',\n type: 'ohif.radioGroup',\n props: {\n type: 'tool',\n icon: 'tool-zoom',\n label: 'Zoom',\n commands: [\n ..._createCommands('setToolActive', 'Zoom', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n },\n },\n {\n id: 'MPR',\n type: 'ohif.action',\n props: {\n type: 'toggle',\n icon: 'icon-mpr',\n label: 'MPR',\n commands: [\n {\n commandName: 'toggleHangingProtocol',\n commandOptions: {\n protocolId: 'mpr',\n },\n context: 'DEFAULT',\n },\n ],\n },\n },\n // Window Level + Presets...\n {\n id: 'WindowLevel',\n type: 'ohif.splitButton',\n props: {\n groupId: 'WindowLevel',\n primary: _createToolButton(\n 'WindowLevel',\n 'tool-window-level',\n 'Window Level',\n [\n ..._createCommands('setToolActive', 'WindowLevel', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n 'Window Level'\n ),\n secondary: {\n icon: 'chevron-down',\n label: 'W/L Manual',\n isActive: true,\n tooltip: 'W/L Presets',\n },\n isAction: true, // ?\n renderer: WindowLevelMenuItem,\n items: [\n _createWwwcPreset(1, 'Soft tissue', '400 / 40'),\n _createWwwcPreset(2, 'Lung', '1500 / -600'),\n _createWwwcPreset(3, 'Liver', '150 / 90'),\n _createWwwcPreset(4, 'Bone', '2500 / 480'),\n _createWwwcPreset(5, 'Brain', '80 / 40'),\n ],\n },\n },\n {\n id: 'Crosshairs',\n type: 'ohif.radioGroup',\n props: {\n type: 'tool',\n icon: 'tool-crosshair',\n label: 'Crosshairs',\n commands: [\n ..._createCommands('setToolActive', 'Crosshairs', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n },\n },\n // Pan...\n {\n id: 'Pan',\n type: 'ohif.radioGroup',\n props: {\n type: 'tool',\n icon: 'tool-move',\n label: 'Pan',\n commands: [\n ..._createCommands('setToolActive', 'Pan', [\n toolGroupIds.CT,\n toolGroupIds.PT,\n toolGroupIds.Fusion,\n // toolGroupIds.MPR,\n ]),\n ],\n },\n },\n {\n id: 'RectangleROIStartEndThreshold',\n type: 'ohif.radioGroup',\n props: {\n type: 'tool',\n icon: 'tool-create-threshold',\n label: 'Rectangle ROI Threshold',\n commands: [\n ..._createCommands('setToolActive', 'RectangleROIStartEndThreshold', [\n toolGroupIds.PT,\n ]),\n {\n commandName: 'displayNotification',\n commandOptions: {\n title: 'RectangleROI Threshold Tip',\n text: 'RectangleROI Threshold tool should be used on PT Axial Viewport',\n type: 'info',\n },\n },\n {\n commandName: 'setViewportActive',\n commandOptions: {\n viewportId: 'ptAXIAL',\n },\n },\n ],\n },\n },\n {\n id: 'fusionPTColormap',\n type: 'ohif.splitButton',\n props: {\n groupId: 'fusionPTColormap',\n primary: _createToolButton(\n 'fusionPTColormap',\n 'tool-fusion-color',\n 'Fusion PT Colormap',\n [],\n 'Fusion PT Colormap'\n ),\n secondary: {\n icon: 'chevron-down',\n label: 'PT Colormap',\n isActive: true,\n tooltip: 'PET Image Colormap',\n },\n isAction: true, // ?\n renderer: WindowLevelMenuItem,\n items: [\n _createColormap('HSV', 'hsv'),\n _createColormap('Hot Iron', 'hot_iron'),\n _createColormap('S PET', 's_pet'),\n _createColormap('Red Hot', 'red_hot'),\n _createColormap('Perfusion', 'perfusion'),\n _createColormap('Rainbow', 'rainbow_2'),\n _createColormap('SUV', 'suv'),\n _createColormap('GE 256', 'ge_256'),\n _createColormap('GE', 'ge'),\n _createColormap('Siemens', 'siemens'),\n ],\n },\n },\n];\n\nexport default toolbarButtons;\n","import packageJson from '../package.json';\n\nconst id = packageJson.name;\n\nexport { id };\n","import { hotkeys, classes } from '@ohif/core';\nimport toolbarButtons from './toolbarButtons.js';\nimport { id } from './id.js';\nimport initToolGroups, { toolGroupIds } from './initToolGroups.js';\nimport setCrosshairsConfiguration from './utils/setCrosshairsConfiguration.js';\nimport setFusionActiveVolume from './utils/setFusionActiveVolume.js';\n\nconst { MetadataProvider } = classes;\n\nconst ohif = {\n layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',\n sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack',\n measurements: '@ohif/extension-default.panelModule.measure',\n thumbnailList: '@ohif/extension-default.panelModule.seriesList',\n};\n\nconst cs3d = {\n viewport: '@ohif/extension-cornerstone.viewportModule.cornerstone',\n};\n\nconst tmtv = {\n hangingProtocol: '@ohif/extension-tmtv.hangingProtocolModule.ptCT',\n petSUV: '@ohif/extension-tmtv.panelModule.petSUV',\n ROIThresholdPanel: '@ohif/extension-tmtv.panelModule.ROIThresholdSeg',\n};\n\nconst extensionDependencies = {\n // Can derive the versions at least process.env.from npm_package_version\n '@ohif/extension-default': '^3.0.0',\n '@ohif/extension-cornerstone': '^3.0.0',\n '@ohif/extension-tmtv': '^3.0.0',\n};\n\nlet unsubscriptions = [];\nfunction modeFactory({ modeConfiguration }) {\n return {\n // TODO: We're using this as a route segment\n // We should not be.\n id,\n routeName: 'tmtv',\n displayName: 'Total Metabolic Tumor Volume',\n /**\n * Lifecycle hooks\n */\n onModeEnter: ({ servicesManager, extensionManager, commandsManager }) => {\n const {\n toolbarService,\n toolGroupService,\n hangingProtocolService,\n displaySetService,\n } = servicesManager.services;\n\n const utilityModule = extensionManager.getModuleEntry(\n '@ohif/extension-cornerstone.utilityModule.tools'\n );\n\n const { toolNames, Enums } = utilityModule.exports;\n\n // Init Default and SR ToolGroups\n initToolGroups(toolNames, Enums, toolGroupService, commandsManager);\n\n const setWindowLevelActive = () => {\n toolbarService.recordInteraction({\n groupId: 'WindowLevel',\n itemId: 'WindowLevel',\n interactionType: 'tool',\n commands: [\n {\n commandName: 'setToolActive',\n commandOptions: {\n toolName: toolNames.WindowLevel,\n toolGroupId: toolGroupIds.CT,\n },\n context: 'CORNERSTONE',\n },\n {\n commandName: 'setToolActive',\n commandOptions: {\n toolName: toolNames.WindowLevel,\n toolGroupId: toolGroupIds.PT,\n },\n context: 'CORNERSTONE',\n },\n {\n commandName: 'setToolActive',\n commandOptions: {\n toolName: toolNames.WindowLevel,\n toolGroupId: toolGroupIds.Fusion,\n },\n context: 'CORNERSTONE',\n },\n ],\n });\n };\n\n const { unsubscribe } = toolGroupService.subscribe(\n toolGroupService.EVENTS.VIEWPORT_ADDED,\n () => {\n // For fusion toolGroup we need to add the volumeIds for the crosshairs\n // since in the fusion viewport we don't want both PT and CT to render MIP\n // when slabThickness is modified\n const {\n displaySetMatchDetails,\n } = hangingProtocolService.getMatchDetails();\n\n setCrosshairsConfiguration(\n displaySetMatchDetails,\n toolNames,\n toolGroupService,\n displaySetService\n );\n\n setFusionActiveVolume(\n displaySetMatchDetails,\n toolNames,\n toolGroupService,\n displaySetService\n );\n\n setWindowLevelActive();\n }\n );\n\n unsubscriptions.push(unsubscribe);\n toolbarService.init(extensionManager);\n toolbarService.addButtons(toolbarButtons);\n toolbarService.createButtonSection('primary', [\n 'MeasurementTools',\n 'Zoom',\n 'WindowLevel',\n 'Crosshairs',\n 'Pan',\n 'RectangleROIStartEndThreshold',\n 'fusionPTColormap',\n ]);\n\n // For the hanging protocol we need to decide on the window level\n // based on whether the SUV is corrected or not, hence we can't hard\n // code the window level in the hanging protocol but we add a custom\n // attribute to the hanging protocol that will be used to get the\n // window level based on the metadata\n hangingProtocolService.addCustomAttribute(\n 'getPTVOIRange',\n 'get PT VOI based on corrected or not',\n props => {\n const ptDisplaySet = props.find(\n imageSet => imageSet.Modality === 'PT'\n );\n\n if (!ptDisplaySet) {\n return;\n }\n\n const { imageId } = ptDisplaySet.images[0];\n const imageIdScalingFactor = MetadataProvider.get(\n 'scalingModule',\n imageId\n );\n\n const isSUVAvailable =\n imageIdScalingFactor && imageIdScalingFactor.suvbw;\n\n if (isSUVAvailable) {\n return {\n windowWidth: 5,\n windowCenter: 2.5,\n };\n }\n\n return;\n }\n );\n },\n onModeExit: ({ servicesManager }) => {\n const {\n toolGroupService,\n syncGroupService,\n segmentationService,\n cornerstoneViewportService,\n } = servicesManager.services;\n\n unsubscriptions.forEach(unsubscribe => unsubscribe());\n toolGroupService.destroy();\n syncGroupService.destroy();\n segmentationService.destroy();\n cornerstoneViewportService.destroy();\n },\n validationTags: {\n study: [],\n series: [],\n },\n isValidMode: ({ modalities, study }) => {\n const modalities_list = modalities.split('\\\\');\n const invalidModalities = ['SM'];\n\n const isValid =\n modalities_list.includes('CT') &&\n modalities_list.includes('PT') &&\n !invalidModalities.some(modality =>\n modalities_list.includes(modality)\n ) &&\n // This is study is a 4D study with PT and CT and not a 3D study for the tmtv\n // mode, until we have a better way to identify 4D studies we will use the\n // StudyInstanceUID to identify the study\n // Todo: when we add the 4D mode which comes with a mechanism to identify\n // 4D studies we can use that\n study.studyInstanceUid !==\n '1.3.6.1.4.1.12842.1.1.14.3.20220915.105557.468.2963630849';\n\n // there should be both CT and PT modalities and the modality should not be SM\n return isValid;\n },\n routes: [\n {\n path: 'tmtv',\n /*init: ({ servicesManager, extensionManager }) => {\n //defaultViewerRouteInit\n },*/\n layoutTemplate: ({ location, servicesManager }) => {\n return {\n id: ohif.layout,\n props: {\n // leftPanels: [ohif.thumbnailList],\n rightPanels: [tmtv.ROIThresholdPanel, tmtv.petSUV],\n viewports: [\n {\n namespace: cs3d.viewport,\n displaySetsToDisplay: [ohif.sopClassHandler],\n },\n ],\n },\n };\n },\n },\n ],\n extensions: extensionDependencies,\n hangingProtocol: tmtv.hangingProtocol,\n sopClassHandlers: [ohif.sopClassHandler],\n hotkeys: [...hotkeys.defaults.hotkeyBindings],\n };\n}\n\nconst mode = {\n id,\n modeFactory,\n extensionDependencies,\n};\n\nexport default mode;\n","import { toolGroupIds } from '../initToolGroups';\n\nexport default function setCrosshairsConfiguration(\n matches,\n toolNames,\n toolGroupService,\n displaySetService\n) {\n const matchDetails = matches.get('ctDisplaySet');\n\n if (!matchDetails) {\n return;\n }\n\n const { SeriesInstanceUID } = matchDetails;\n const displaySets = displaySetService.getDisplaySetsForSeries(\n SeriesInstanceUID\n );\n\n const toolConfig = toolGroupService.getToolConfiguration(\n toolGroupIds.Fusion,\n toolNames.Crosshairs\n );\n\n const crosshairsConfig = {\n ...toolConfig,\n filterActorUIDsToSetSlabThickness: [displaySets[0].displaySetInstanceUID],\n };\n\n toolGroupService.setToolConfiguration(\n toolGroupIds.Fusion,\n toolNames.Crosshairs,\n crosshairsConfig\n );\n}\n","import { toolGroupIds } from '../initToolGroups';\n\nexport default function setFusionActiveVolume(\n matches,\n toolNames,\n toolGroupService,\n displaySetService\n) {\n const matchDetails = matches.get('ptDisplaySet');\n\n if (!matchDetails) {\n return;\n }\n\n const { SeriesInstanceUID } = matchDetails;\n\n const displaySets = displaySetService.getDisplaySetsForSeries(\n SeriesInstanceUID\n );\n\n if (!displaySets || displaySets.length === 0) {\n return;\n }\n\n const wlToolConfig = toolGroupService.getToolConfiguration(\n toolGroupIds.Fusion,\n toolNames.WindowLevel\n );\n\n const ellipticalToolConfig = toolGroupService.getToolConfiguration(\n toolGroupIds.Fusion,\n toolNames.EllipticalROI\n );\n\n // Todo: this should not take into account the loader id\n const volumeId = `cornerstoneStreamingImageVolume:${displaySets[0].displaySetInstanceUID}`;\n\n const windowLevelConfig = {\n ...wlToolConfig,\n volumeId,\n };\n\n const ellipticalROIConfig = {\n ...ellipticalToolConfig,\n volumeId,\n };\n\n toolGroupService.setToolConfiguration(\n toolGroupIds.Fusion,\n toolNames.WindowLevel,\n windowLevelConfig\n );\n\n toolGroupService.setToolConfiguration(\n toolGroupIds.Fusion,\n toolNames.EllipticalROI,\n ellipticalROIConfig\n );\n}\n"],"names":["root","factory","exports","module","require","define","amd","globalThis","__WEBPACK_EXTERNAL_MODULE__762__","__WEBPACK_EXTERNAL_MODULE__369__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","d","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","toolGroupIds","CT","PT","Fusion","MIP","default","toolNames","Enums","toolGroupService","commandsManager","tools","active","toolName","WindowLevel","bindings","mouseButton","MouseBindings","Primary","Pan","Auxiliary","Zoom","Secondary","StackScrollMouseWheel","passive","Length","ArrowAnnotate","Bidirectional","DragProbe","Probe","EllipticalROI","RectangleROI","StackScroll","Angle","CobbAngle","Magnify","enabled","SegmentationDisplay","disabled","Crosshairs","toolsConfig","viewportIndicators","autoPan","panSize","getTextCallback","callback","eventDetails","runCommand","changeTextCallback","data","createToolGroupAndAddTools","mipTools","VolumeRotateMouseWheel","MipJumpToClick","mipToolsConfig","rotateIncrementDegrees","targetViewportIds","_initToolGroups","windowLevelPresets","defaults","_createButton","type","id","icon","label","commands","tooltip","_createColormap","colormap","toString","title","subtitle","commandName","commandOptions","toolGroupId","bind","_createToolButton","_createWwwcPreset","preset","context","_createCommands","map","props","groupId","isRadio","primary","secondary","isActive","items","protocolId","isAction","renderer","WindowLevelMenuItem","text","viewportId","packageJson","MetadataProvider","classes","ohif","cs3d","tmtv","extensionDependencies","unsubscriptions","modeFactory","_ref","modeConfiguration","routeName","displayName","onModeEnter","_ref2","servicesManager","extensionManager","toolbarService","hangingProtocolService","displaySetService","services","utilityModule","getModuleEntry","initToolGroups","unsubscribe","subscribe","EVENTS","VIEWPORT_ADDED","displaySetMatchDetails","getMatchDetails","matches","matchDetails","SeriesInstanceUID","displaySets","getDisplaySetsForSeries","crosshairsConfig","getToolConfiguration","filterActorUIDsToSetSlabThickness","displaySetInstanceUID","setToolConfiguration","setCrosshairsConfiguration","length","wlToolConfig","ellipticalToolConfig","volumeId","windowLevelConfig","ellipticalROIConfig","setFusionActiveVolume","recordInteraction","itemId","interactionType","push","init","addButtons","toolbarButtons","createButtonSection","addCustomAttribute","ptDisplaySet","find","imageSet","Modality","imageId","images","imageIdScalingFactor","suvbw","windowWidth","windowCenter","onModeExit","_ref3","syncGroupService","segmentationService","cornerstoneViewportService","forEach","destroy","validationTags","study","series","isValidMode","_ref4","modalities","modalities_list","split","includes","some","modality","studyInstanceUid","routes","path","layoutTemplate","_ref5","location","rightPanels","viewports","namespace","displaySetsToDisplay","extensions","hangingProtocol","sopClassHandlers","hotkeys"],"sourceRoot":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ohif/mode-tmtv",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Total Metabolic Tumor Volume Workflow",
|
|
5
|
+
"author": "OHIF",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": "OHIF/Viewers",
|
|
8
|
+
"main": "dist/ohif-mode-tmtv.umd.js",
|
|
9
|
+
"module": "src/index.js",
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=14",
|
|
12
|
+
"npm": ">=6",
|
|
13
|
+
"yarn": ">=1.16.0"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"ohif-mode"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
|
|
27
|
+
"dev:cornerstone": "yarn run dev",
|
|
28
|
+
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
|
|
29
|
+
"build:package": "yarn run build",
|
|
30
|
+
"start": "yarn run dev",
|
|
31
|
+
"test:unit": "jest --watchAll",
|
|
32
|
+
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@ohif/core": "^3.0.0",
|
|
36
|
+
"@ohif/extension-default": "^3.0.0",
|
|
37
|
+
"@ohif/extension-cornerstone": "^3.0.0",
|
|
38
|
+
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
|
39
|
+
"@ohif/extension-dicom-pdf": "^3.0.1",
|
|
40
|
+
"@ohif/extension-dicom-video": "^3.0.1",
|
|
41
|
+
"@ohif/extension-measurement-tracking": "^3.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@babel/runtime": "^7.20.13"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"webpack": "^5.50.0",
|
|
48
|
+
"webpack-merge": "^5.7.3"
|
|
49
|
+
}
|
|
50
|
+
}
|