measure.f7.html 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585
  1. <template>
  2. <div class="page" data-name="home-measure">
  3. <div class="iimt-top-container iimt-hexpand">
  4. <div class="home-patient-data-grid" style="flex-direction:row; padding-bottom: 0px; height: calc(100% - 16px);">
  5. <div class="iimt-frame" style="width:200px; height:calc(100% - 2px); ">
  6. <div class="title"><span>{{js "global.tr[global.lang].measure.media"}}</span></div>
  7. <div class="content" style="margin-top:8px; flex-direction: column; justify-content: flex-start; overflow-y:scroll;">
  8. {{#each media}}
  9. <a href="#" @click="setMedia({{@index}})" class="flex column centerh measure-media" style="min-height:fit-content; height:auto; position:relative;">
  10. {{#js_if "this.metrics.fps"}}
  11. {{#js_if "this.filename.indexOf('.dicom')===-1"}}
  12. <video id="media{{@index}}" src="{{js "app.data.config.storageBaseURL+'/media/'+app.data.patient.visitID+'/'+this.filename"}}" style="max-width:calc(100% - 16px);"></video>
  13. {{else}}
  14. <video id="media{{@index}}" src="{{js "app.data.config.storageBaseURL+'/media/'+app.data.patient.visitID+'/'+this.filename.replace('.dicom','.mp4')"}}" style="max-width:calc(100% - 16px);"></video>
  15. {{/js_if}}
  16. <img src="static/images/lens.png" style="position:absolute; top:0; right:0;">
  17. {{else}}
  18. {{#js_if "this.filename.indexOf('.dicom')===-1"}}
  19. <img id="media{{@index}}" src="{{js "app.data.config.storageBaseURL+'/media/'+app.data.patient.visitID+'/'+this.filename"}}" style="max-width:calc(100% - 16px);">
  20. {{else}}
  21. <img id="media{{@index}}" src="{{js "app.data.config.storageBaseURL+'/media/'+app.data.patient.visitID+'/'+this.filename.replace('.dicom','.jpeg')"}}" style="max-width:calc(100% - 16px);">
  22. {{/js_if}}
  23. {{/js_if}}
  24. <span id="media-description{{@index}}" class="iimt-subtitle">
  25. {{js "eval('global.tr[global.lang].arteries.'+../area+'.'+(this.side.length?this.side+'.':'')+this.location)"}}
  26. </span>
  27. </a>
  28. {{/each}}
  29. </div>
  30. </div>
  31. <div style="display:flex; flex-shrink:1; flex-direction:column; width:calc(100% - 200px - 200px - 34px); height: 100%; flex-wrap: nowrap; margin:0 16px;">
  32. <div class="iimt-frame-grow" style="margin-bottom:16px; position:relative;">
  33. <div class="flex row centerh centerv" style="height: 32px; position:absolute; top:0px; width:100%;">
  34. <img src='{{js "'static/images/gender_'+this.patient.gender+'.png'"}}' style="height: 22px;">&nbsp;{{patient.lastname}} {{patient.firstname}}
  35. </div>
  36. <div id="mouseCoord" style="position: absolute; top:0px; right:0px; padding-right:2px; color:gray;"></div>
  37. <div class="title"><span>{{js "global.tr[global.lang].measure.image"}}</span></div>
  38. <div class="content" style="margin-top:8px; flex-direction: column; justify-content: flex-start;">
  39. <span id="media-description" class="iimt-subtitle"></span>
  40. <div id="media-container" style="position: relative; margin-bottom: 8px;">
  41. <video id="measure-video" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 100;"></video>
  42. <canvas id="measure-canvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 102;" @dblclick="dblclick" @mousedown="mousedown" @mouseup="mouseup" @mouseout="mouseout" @mousemove="mousemove"></canvas>
  43. </div>
  44. </div>
  45. </div>
  46. <div class="iimt-frame-grow" style="overflow: hidden; height:75px;">
  47. <div class="title"><span>{{js "global.tr[global.lang].measure.measures"}}</span></div>
  48. <div class="content" style="margin-top:0px; flex-direction: column; justify-content: flex-start;">
  49. <div class="data-table">
  50. <table id="measure-list">
  51. <!--thead class="iimt-small-row">
  52. <tr>
  53. <th class="label-cell" style="width: 200px;">{{js "global.tr[global.lang].measure.type"}}</th>
  54. <th class="label-cell">{{js "global.tr[global.lang].measure.values"}}</th>
  55. </tr>
  56. </thead-->
  57. <tbody class="iimt-small-row" style="overflow-y: unset;">
  58. </tbody>
  59. </table>
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. <div style="display:flex; flex-shrink:1; flex-direction:column; width:200px; height: 100%;">
  65. <div class="iimt-frame" style="height: 72px; margin-bottom: 16px;">
  66. <div class="title"><span>{{js "global.tr[global.lang].measure.tools"}}</span></div>
  67. <div class="content" style="margin-top:8px; flex-direction: column; justify-content: flex-start;">
  68. <a href="#" id="measure-calibration" class="button iimt-button disabled" style="width:calc(100% - 16px);" @click="calibrationAction">{{js "global.tr[global.lang].measure.calibration"}}</a>
  69. </div>
  70. </div>
  71. <div class="iimt-frame" style="height: 210px; margin-bottom: 16px;">
  72. <div class="title"><span>{{js "global.tr[global.lang].measure.measures"}}</span></div>
  73. <div class="content" style="margin-top:8px; flex-direction: column; justify-content: space-around;">
  74. <a href="#" id="measure-imt" class="button iimt-button disabled" style="width:calc(100% - 16px);" @click="imtAction">{{js "global.tr[global.lang].measure.imt"}}</a>
  75. <a href="#" id="measure-plaque" class="button iimt-button disabled" style="width:calc(100% - 16px);" @click="plaqueAction">{{js "global.tr[global.lang].measure.plaque"}}</a>
  76. <a href="#" id="measure-distance" class="button iimt-button disabled" style="width:calc(100% - 16px);" @click="distanceAction">{{js "global.tr[global.lang].measure.distance"}}</a>
  77. <a href="#" id="measure-area" class="button iimt-button disabled" style="width:calc(100% - 16px);" @click="areaAction">{{js "global.tr[global.lang].measure.area"}}</a>
  78. </div>
  79. </div>
  80. <div class="iimt-frame" style="margin-bottom: 16px; height:calc(100% - 74px - 212px - 78px - 50px);">
  81. <div class="title"><span>{{js "global.tr[global.lang].measure.segment"}}</span></div>
  82. <div id="media-segment" class="content" style="margin-top:8px; flex-direction: column; justify-content: flex-start;">
  83. </div>
  84. </div>
  85. <div class="iimt-frame" style="height: 76px;">
  86. <div class="title"><span>{{js "global.tr[global.lang].measure.data"}}</span></div>
  87. <div class="content" style="margin-top:8px; flex-direction: column; justify-content: flex-start;">
  88. <a href="#" @click="allMeasures" class="button iimt-button" style="width:calc(100% - 16px);">{{js "global.tr[global.lang].measure.allMeasures"}}</a>
  89. </div>
  90. </div>
  91. </div>
  92. </div>
  93. </div>
  94. </div>
  95. </template>
  96. <script>
  97. export default {
  98. on: {
  99. pageInit: function() {
  100. $$('.measure-media').removeClass('active');
  101. $$('#media0').parent().addClass('active');
  102. $$('#media-description').text($$('#media-description0').text());
  103. $$('#media0').parent().click();
  104. this.actionStatus = null;
  105. this.points = [];
  106. this.updateMeasureList();
  107. }
  108. },
  109. methods: {
  110. scaleIn: function(x) {
  111. return Math.round(x / this.zoom);
  112. },
  113. scaleOut: function(x) {
  114. return Math.round(x * this.zoom);
  115. },
  116. updateButtonStates: function() {
  117. $$('#measure-calibration').removeClass('disabled');
  118. $$('#measure-imt').removeClass('disabled');
  119. $$('#measure-plaque').removeClass('disabled');
  120. $$('#measure-distance').removeClass('disabled');
  121. $$('#measure-area').removeClass('disabled');
  122. if(this.media[this.currentMediaIndex].metrics.pxwidth==0 || this.media[this.currentMediaIndex].metrics.pxheight==0) {
  123. $$('#measure-imt').addClass('disabled');
  124. $$('#measure-plaque').addClass('disabled');
  125. $$('#measure-distance').addClass('disabled');
  126. $$('#measure-area').addClass('disabled');
  127. }
  128. let isDicom = (this.media[this.currentMediaIndex].filename.indexOf('.dicom')!==-1);
  129. if(isDicom &&
  130. (this.media[this.currentMediaIndex].metrics.pxwidth!=0 && this.media[this.currentMediaIndex].metrics.pxheight!=0)) {
  131. $$('#measure-calibration').addClass('disabled');
  132. }
  133. else {
  134. let onlyCalibration = true;
  135. for(let i=0; i<this.media[this.currentMediaIndex].measure.length; ++i) {
  136. if(this.media[this.currentMediaIndex].measure[i].type!='calibration') {
  137. onlyCalibration = false;
  138. }
  139. }
  140. if(!onlyCalibration) {
  141. $$('#measure-calibration').addClass('disabled');
  142. }
  143. }
  144. },
  145. getImageData: function() {
  146. // video
  147. if(this.videoViewer) {
  148. let ctx = $$('#measure-canvas')[0].getContext('2d');
  149. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  150. ctx.scale(this.zoom, this.zoom);
  151. ctx.drawImage(this.currentMedia, 0, 0);
  152. this.imageData = ctx.getImageData(0, 0, this.imgWidth, this.imgHeight);
  153. ctx.setTransform(1, 0, 0, 1, 0, 0);
  154. this.update();
  155. return this.imageData;
  156. }
  157. // image
  158. return this.imageData;
  159. },
  160. setMedia: function(idx) {
  161. console.log('set media', this.media[idx]);
  162. let that = this;
  163. $$('.measure-media').removeClass('active');
  164. $$('#media'+idx).parent().addClass('active');
  165. $$('#media-description').text($$('#media-description'+idx).text());
  166. let media = $$('#media'+idx);
  167. let img = null;
  168. let container = $$('#media-container');
  169. let video = $$('video#measure-video');
  170. video[0].setAttribute('crossOrigin', '');
  171. let canvas = $$('canvas#measure-canvas');
  172. let maxW = 768;
  173. let maxH = 520;
  174. // cleanup
  175. if(this.videoViewer) {
  176. console.log('clean video viewer');
  177. this.videoViewer.clean();
  178. delete this.videoViewer;
  179. this.videoViewer = null;
  180. }
  181. // load
  182. let ctx = canvas[0].getContext('2d');
  183. // video
  184. if(this.media[idx].metrics.fps) {
  185. // load
  186. video.on('loadeddata', function() {
  187. that.zoom = 1.0 / (video[0].videoHeight/maxH);
  188. that.imgWidth = that.scaleOut(video[0].videoWidth);
  189. that.imgHeight = that.scaleOut(video[0].videoHeight);
  190. console.log('zoom', that.zoom);
  191. container.css({
  192. width: that.imgWidth+'px',
  193. height: that.imgHeight+'px'
  194. });
  195. canvas.css({
  196. width: that.imgWidth+'px',
  197. height: that.imgHeight+'px'
  198. });
  199. ctx.canvas.width=that.imgWidth;
  200. ctx.canvas.height=that.imgHeight;
  201. that.videoViewer = new global.VideoViewer(video, that.media[idx].metrics.fps, that.media[idx].metrics.frameCount, that.videoFrameChanged);
  202. that.currentMedia = video[0];
  203. that.update();
  204. video.off('loadeddata');
  205. });
  206. }
  207. // image
  208. else {
  209. img = new Image();
  210. img.setAttribute('crossOrigin', '');
  211. img.onload = function() {
  212. that.zoom = 1.0 / (img.height/maxH);
  213. that.imgWidth = that.scaleOut(img.width);
  214. that.imgHeight = that.scaleOut(img.height);
  215. console.log('zoom', that.zoom);
  216. container.css({
  217. width: that.imgWidth+'px',
  218. height: that.imgHeight+'px'
  219. });
  220. canvas.css({
  221. width: that.imgWidth+'px',
  222. height: that.imgHeight+'px'
  223. });
  224. ctx.canvas.width=that.imgWidth;
  225. ctx.canvas.height=that.imgHeight;
  226. ctx.scale(that.zoom, that.zoom);
  227. ctx.drawImage(img, 0, 0);
  228. that.imageData = ctx.getImageData(0, 0, that.imgWidth, that.imgHeight);
  229. ctx.setTransform(1, 0, 0, 1, 0, 0);
  230. that.currentMedia = img;
  231. that.update();
  232. };
  233. }
  234. this.currentMediaIndex = idx;
  235. this.calibrationHover = null;
  236. // DICOM
  237. if(this.media[idx].filename.indexOf('.dicom')!==-1) {
  238. //console.log('dicom', this.media[idx]);
  239. this.addDicomCalibration(idx);
  240. }
  241. //
  242. that.updateButtonStates();
  243. that.mouseout();
  244. // segment
  245. let path = 'area_'+this.area+'_'+(this.media[idx].side=='left'?'L':(this.media[idx].side=='right'?'R':'R'))+'_'+this.media[idx].location+'.png';
  246. $$('#media-segment').html('<img src="static/images/'+path+'" style="max-height:90%; max-width:90%;">');
  247. this.updateMeasureList();
  248. // load media
  249. if(img) {
  250. img.src = media.attr('src');
  251. }
  252. else {
  253. video.attr('src', media.attr('src'));
  254. }
  255. },
  256. addDicomCalibration: function(idx) {
  257. let hasCalibration = false;
  258. for(let m=0; m<this.media[idx].measure.length; ++m) {
  259. if(this.media[idx].measure[m].type=='calibration') {
  260. hasCalibration = true;
  261. break;
  262. }
  263. }
  264. if(!hasCalibration && this.media[idx].metrics.pxwith!=0 && this.media[idx].metrics.pxheight!=0) {
  265. this.media[idx].measure.push({
  266. ID: 0,
  267. type: 'calibration',
  268. computation: {
  269. distance: 20
  270. },
  271. fk_media: 0,
  272. fk_user: 0,
  273. points: [{
  274. x: this.media[idx].metrics.width-40,
  275. y: 50
  276. }, {
  277. x: this.media[idx].metrics.width-40,
  278. y: (50 + 20 / this.media[idx].metrics.pxheight)
  279. }]
  280. });
  281. }
  282. },
  283. updateMeasureList: function() {
  284. let txt = '';
  285. let isDicom = (this.media[this.currentMediaIndex].filename.indexOf('.dicom')!==-1);
  286. for (let i=0; i<this.media[this.currentMediaIndex].measure.length; ++i) {
  287. let style = '';
  288. let M = this.media[this.currentMediaIndex].measure[i];
  289. //console.log(M);
  290. let type = '';
  291. /*if(M.type=='calibration') type = global.tr[global.lang].measure.calibration;
  292. else */if(M.type=='area') type = global.tr[global.lang].measure.area;
  293. else if(M.type=='thickness') type = global.tr[global.lang].measure.thickness;
  294. else if(M.type=='diameter') type = global.tr[global.lang].measure.diameter;
  295. else if(M.type=='plaque_thickness') type = global.tr[global.lang].measure.plaqueThickness;
  296. else if(M.type=='imt') type = global.tr[global.lang].measure.imt;
  297. else if(M.type=='plaque') type = global.tr[global.lang].measure.plaque;
  298. let data = '';
  299. /*if(M.type=='calibration') data += parseFloat(M.computation.distance).toFixed(0)+' mm';
  300. else */if (M.type=='thickness' || M.type=='diameter' || M.type=='plaque_thickness') data += parseFloat(M.computation.distance).toFixed(2)+' mm';
  301. else if(M.type=='area') data += parseFloat(M.computation.surface).toFixed(2)+' mm²';
  302. else if(M.type=='imt') {
  303. data += global.tr[global.lang].measure.imtDist+': '+parseFloat(M.computation.distance).toFixed(2)+' mm, ';
  304. data += global.tr[global.lang].measure.imtMean+': '+parseFloat(M.computation.imt_mean).toFixed(3)+' mm, ';
  305. data += global.tr[global.lang].measure.imtStddev+': '+parseFloat(M.computation.imt_stddev).toFixed(2)+' mm, ';
  306. data += global.tr[global.lang].measure.imtMax+': '+parseFloat(M.computation.imt_max).toFixed(3)+' mm, ';
  307. data += global.tr[global.lang].measure.imtIQ+': '+parseFloat(M.computation.qualityIndex).toFixed(2)+', ';
  308. data += global.tr[global.lang].measure.imtPoints+': '+M.computation.numberOfPoints+', ';
  309. data += (M.computation.nearWall?global.tr[global.lang].measure.imtProximal:global.tr[global.lang].measure.imtDistal);
  310. if(M.computation.qualityIndex<=0.3 || M.computation.distance<=8) {
  311. style = 'style="color: red;"';
  312. }
  313. }
  314. else if(M.type=='plaque') {
  315. data += global.tr[global.lang].measure.plaqueArea+': '+parseFloat(M.computation.plaque_area).toFixed(2)+' mm², ';
  316. //data += global.tr[global.lang].measure.plaqueMean+': '+parseFloat(M.computation.plaque_mean_density).toFixed(0)+', ';
  317. data += global.tr[global.lang].measure.plaqueThkmax+': '+parseFloat(M.computation.plaque_max_thickness).toFixed(2)+' mm, ';
  318. data += global.tr[global.lang].measure.plaqueThkmean+': '+parseFloat(M.computation.plaque_mean_thickness).toFixed(2)+' mm, ';
  319. data += global.tr[global.lang].measure.plaqueLength+': '+parseFloat((M.computation.longeantPlaque[M.computation.longeantPlaque.length-1].x-M.computation.longeantPlaque[0].x)*this.media[this.currentMediaIndex].metrics.pxwidth).toFixed(2)+' mm, ';
  320. data += global.tr[global.lang].measure.plaquePoints+': '+parseFloat(M.computation.numberOfMeasures).toFixed(0);
  321. }
  322. if(M.type!='calibration') {
  323. txt += '<tr>'+
  324. '<td class="label-cell" style="width: 160px; padding-right: 0px;">'+type+'</td>'+
  325. '<td class="label-cell" '+style+'>'+data+'</td>'+
  326. '</tr>';
  327. }
  328. }
  329. $$('#measure-list tbody').html('').html(txt);
  330. },
  331. allMeasures: function() {
  332. let txt = '';
  333. for (let media=0; media<this.media.length; ++media) {
  334. let mediaStr = '';
  335. if(this.media[media].metrics.fps) {
  336. if(this.media[media].filename.indexOf('.dicom')===-1) {
  337. mediaStr = '<video style="width:192px; padding:4px;" src="'+app.data.config.storageBaseURL+'/media/'+app.data.patient.visitID+'/'+this.media[media].filename+'"></video>';
  338. }
  339. else {
  340. mediaStr = '<video style="width:192px; padding:4px;" src="'+app.data.config.storageBaseURL+'/media/'+app.data.patient.visitID+'/'+this.media[media].filename.replace('.dicom','.mp4')+'"></video>';
  341. }
  342. }
  343. else {
  344. if(this.media[media].filename.indexOf('.dicom')===-1) {
  345. mediaStr = '<img style="width:192px; padding:4px;" src="'+app.data.config.storageBaseURL+'/media/'+app.data.patient.visitID+'/'+this.media[media].filename+'">';
  346. }
  347. else {
  348. mediaStr = '<img style="width:192px; padding:4px;" src="'+app.data.config.storageBaseURL+'/media/'+app.data.patient.visitID+'/'+this.media[media].filename.replace('.dicom','.jpeg')+'">';
  349. }
  350. }
  351. let loc = eval('global.tr[global.lang].arteries.'+this.area+'.'+(this.media[media].side.length?this.media[media].side+'.':'')+this.media[media].location);
  352. console.log(loc);
  353. let tab = loc.split(' ');
  354. let locT = [];
  355. for(let t=0; t<tab.length; t++) {
  356. locT.push(tab[t].toUpperCase().charAt(0));
  357. }
  358. let calibration = '';
  359. for (let i=0; i<this.media[media].measure.length; ++i) {
  360. let M = this.media[media].measure[i];
  361. if(M.type=='calibration') {
  362. calibration = parseFloat(M.computation.distance).toFixed(0)+' mm';
  363. }
  364. }
  365. for (let i=0; i<this.media[media].measure.length; ++i) {
  366. let mHead = '';
  367. let mData = '';
  368. let M = this.media[media].measure[i];
  369. if(M.type=='plaque') {
  370. mHead = '<tr>'+
  371. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.plaqueArea+'</th>'+
  372. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.plaqueThkmax+'</th></tr>';
  373. mData += '<tr>';
  374. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.plaque_area).toFixed(1)+' mm²</td>';
  375. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.plaque_max_thickness).toFixed(2)+' mm</td>';
  376. mData += '</tr>';
  377. }
  378. else if(M.type=='area') {
  379. mHead = '<tr>'+
  380. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.area+'</th></tr>';
  381. mData += '<tr>';
  382. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.surface).toFixed(2)+' mm²</td>';
  383. mData += '</tr>';
  384. }
  385. else if(M.type=='thickness') {
  386. mHead = '<tr>'+
  387. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.thickness+'</th></tr>';
  388. mData += '<tr>';
  389. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.distance).toFixed(2)+' mm</td>';
  390. mData += '</tr>';
  391. }
  392. else if(M.type=='diameter') {
  393. mHead = '<tr>'+
  394. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.diameter+'</th></tr>';
  395. mData += '<tr>';
  396. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.distance).toFixed(2)+' mm</td>';
  397. mData += '</tr>';
  398. }
  399. else if(M.type=='plaque_thickness') {
  400. mHead = '<tr>'+
  401. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.plaque_thickness+'</th></tr>';
  402. mData += '<tr>';
  403. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.distance).toFixed(2)+' mm</td>';
  404. mData += '</tr>';
  405. }
  406. else if(M.type=='imt') {
  407. mHead = '<tr>'+
  408. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.imtMean+'</th>'+
  409. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.imtMax+'</th>'+
  410. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.imtStddev+'</th>'+
  411. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.imtIQ+'</th>'+
  412. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.imtDist+'</th>'+
  413. '<th style="padding:0; font-size:10px;">'+global.tr[global.lang].measure.imtPoints+'</th></tr>';
  414. mData += '<tr>';
  415. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.imt_mean).toFixed(3)+' mm</td>';
  416. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.imt_max).toFixed(3)+' mm</td>';
  417. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.imt_stddev).toFixed(3)+' mm</td>';
  418. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.qualityIndex).toFixed(3)+'</td>';
  419. mData += '<td style="padding:0; font-size:14px;">'+parseFloat(M.computation.distance).toFixed(3)+' mm</td>';
  420. mData += '<td style="padding:0; font-size:14px;">'+M.computation.numberOfPoints+'</td>';
  421. mData += '</tr>';
  422. }
  423. if(mHead.length) {
  424. txt += '<tr>'+
  425. '<td class="" style="width: 200px; padding: 0;">'+mediaStr+'</td>'+
  426. '<td class="" style="width: 20px; padding: 0; font-weight: 600;">'+locT.join('<br>')+'</td>'+
  427. '<td class="" style="padding: 0;"><table><thead style="overflow-y:hidden;">'+mHead+'</thead><tbody style="overflow-y:hidden;">'+mData+'</tbody></table></td>'+
  428. '<td class="" style="width: 70px; padding: 0;">'+calibration+'</td>'+
  429. '</tr>';
  430. }
  431. }
  432. }
  433. let completed = '';
  434. if(global.app.data.user.type=='reader') {
  435. completed += '<div class="row" style="justify-content:center">';
  436. completed += '<a id="complete-timepoint" href="#" class="button button-fill color-theme-red" style="font-weight: 600;">'+global.tr[global.lang].measure.complete+'</a>';
  437. completed += '</div>';
  438. }
  439. // Create dynamic Popup
  440. app.popup.create({
  441. content: '<div class="popup measures">'+
  442. '<div class="dialog-title" style="display:flex; flex-shrink:1; justify-content:space-between;">'+global.tr[global.lang].measure.allMeasures+' <a href="#" class="link popup-close" style="color:white; padding-right:8px;">'+global.tr[global.lang].topLevel.close+'</a></div>'+
  443. '<div class="block" style="margin:0; height: calc(100% - 24px); overflow-y: scroll;">'+
  444. '<div class="data-table">'+
  445. '<table>'+
  446. '<thead class="iimt-small-row">'+
  447. '<tr>'+
  448. '<th class="" style="width: 200px; padding: 0;">Media</th>'+
  449. '<th class="" style="width: 20px; padding: 0;">&nbsp;</th>'+
  450. '<th class="" style="padding: 0;">'+global.tr[global.lang].measure.measures+'</th>'+
  451. '<th class="" style="width: 70px; padding: 0;">'+global.tr[global.lang].measure.calibration+'</th>'+
  452. '</tr>'+
  453. '</thead>'+
  454. '<tbody class="iimt-small-row" style="overflow-y: unset;">'+
  455. txt+
  456. '</tbody>'+
  457. '</table>'+
  458. completed+
  459. '</div>'+
  460. '</div>'+
  461. '</div>',
  462. on: {
  463. opened: function () {
  464. let comp = $$("#complete-timepoint");
  465. if(comp) {
  466. comp.click(function() {
  467. app.dialog.password(global.tr[global.lang].measure.confirmPassword, null, function(password) {
  468. let postData = {
  469. password: password,
  470. visitID: app.data.patient.visitID,
  471. apiKey: ''
  472. };
  473. app.preloader.show();
  474. app.request.post(app.data.config.apiBaseURL + '/measure/complete/', postData, function (data) {
  475. console.log('measure/complete', data);
  476. app.preloader.hide();
  477. if (data.result == 'ERROR') {
  478. switch (data.reason) {
  479. case 'denied':
  480. app.methods.signout(global.tr[global.lang].topLevel.warning.disconnected);
  481. break;
  482. case 'bad_password':
  483. app.dialog.alert(global.tr[global.lang].topLevel.error.wrong_credentials);
  484. break;
  485. default:
  486. app.dialog.alert(global.tr[global.lang].topLevel.error.internal_error);
  487. break;
  488. }
  489. }
  490. else {
  491. app.popup.close();
  492. // return to patient
  493. $$('#toolbar-patient').click();
  494. }
  495. }, function (data) {
  496. console.log('error', data);
  497. }, 'json');
  498. });
  499. });
  500. }
  501. }
  502. }
  503. }).open();
  504. },
  505. videoFrameChanged: function(frame) {
  506. console.log(frame);
  507. console.log(this);
  508. this.update();
  509. },
  510. update: function() {
  511. let ctx = $$('#measure-canvas')[0].getContext('2d');
  512. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  513. ctx.scale(this.zoom, this.zoom);
  514. if(!this.videoViewer) {
  515. ctx.drawImage(this.currentMedia, 0, 0/*, ctx.canvas.width, ctx.canvas.height*/);
  516. }
  517. if(global.app.data.user.type=='reader' && this.anon_percent!=0) {
  518. ctx.fillStyle = "#000000";
  519. ctx.fillRect(0, 0, ctx.canvas.width/this.zoom, (ctx.canvas.height/100*this.anon_percent)/this.zoom);
  520. }
  521. console.log("actionStatus", this.actionStatus);
  522. console.log(this.media[this.currentMediaIndex].measure);
  523. // drawing calibration
  524. if((this.actionStatus=='calibration' && this.points.length==2) || this.actionStatus=='calibrationConfirm') {
  525. this.drawCalibation(ctx, this.points, null);
  526. }
  527. // drawing distance
  528. if(this.actionStatus=='distance' && this.points.length==2) {
  529. this.drawDistance(ctx, this.points, null);
  530. }
  531. // drawing area
  532. if(this.actionStatus=='area' && this.points.length>=2) {
  533. this.drawArea(ctx, this.points, null);
  534. }
  535. // drawing imt
  536. if(this.actionStatus=='imt' && this.points.length==2) {
  537. this.drawImt(ctx, this.points, null);
  538. }
  539. // drawing plaque
  540. if((this.actionStatus=='plaque' || this.actionStatus=='plaque2') && this.points.length>=2) {
  541. this.drawPlaque(ctx, this.points, null);
  542. }
  543. // known measures
  544. for(let i=0; i<this.media[this.currentMediaIndex].measure.length; ++i) {
  545. let M = this.media[this.currentMediaIndex].measure[i];
  546. if(M.type=='calibration') {
  547. if(this.actionStatus!='calibration' && this.actionStatus!='calibrationConfirm') {
  548. this.drawCalibation(ctx, M.points, M.computation);
  549. }
  550. }
  551. else if(!this.videoViewer || (this.videoViewer && M.frame==this.videoViewer.currentFrame)) {
  552. if(!this.actionStatus && (M.type=='thickness' || M.type=='diameter' || M.type=='plaque_thickness')) {
  553. this.drawDistance(ctx, M.points, M.computation);
  554. }
  555. else if(!this.actionStatus && M.type=='area') {
  556. this.drawArea(ctx, M.points, M.computation);
  557. }
  558. else if(!this.actionStatus && M.type=='imt') {
  559. this.drawImt(ctx, M.points, M.computation);
  560. }
  561. else if(!this.actionStatus && M.type=='plaque') {
  562. this.drawPlaque(ctx, M.points, M.computation);
  563. }
  564. }
  565. }
  566. ctx.setTransform(1, 0, 0, 1, 0, 0);
  567. },
  568. drawImt: function(ctx, points, computation) {
  569. if(computation) {
  570. for(let i=0; i<4; i++) {
  571. ctx.strokeStyle = '#00ff00';
  572. for(let p=0; p<computation.vect_adventitia.length; ++p) {
  573. ctx.beginPath();
  574. ctx.moveTo(computation.vect_adventitia[p].x , computation.vect_adventitia[p].y);
  575. ctx.lineTo(computation.vect_adventitia[p].x+1, computation.vect_adventitia[p].y);
  576. ctx.stroke();
  577. }
  578. ctx.strokeStyle = '#FFA500';
  579. for(let p=0; p<computation.vect_intima.length; ++p) {
  580. ctx.beginPath();
  581. ctx.moveTo(computation.vect_intima[p].x , computation.vect_intima[p].y);
  582. ctx.lineTo(computation.vect_intima[p].x+1, computation.vect_intima[p].y);
  583. ctx.stroke();
  584. }
  585. }
  586. /*ctx.strokeStyle = '#00ffff';
  587. for(let p=0; p<computation.vect_media.length; ++p) {
  588. ctx.beginPath();
  589. ctx.moveTo(computation.vect_media[p].x, computation.vect_media[p].y);
  590. ctx.lineTo(computation.vect_media[p].x+1, computation.vect_media[p].y);
  591. ctx.stroke();
  592. }*/
  593. /*let pa = computation.vect_intima[computation.vect_intima.length-1];
  594. let pm = computation.vect_media[computation.vect_media.length-1];
  595. let imt_mean = Math.round(computation.imt_mean*100)/100;
  596. ctx.font = '16px sans-serif';
  597. ctx.textAlign = 'left';
  598. ctx.strokeStyle = '#FFA500';
  599. ctx.strokeText(imt_mean+' mm', Math.max(pa.x,pm.x)+8, (pa.y+pm.y)/2+8);
  600. ctx.fillStyle = "#00ff00";
  601. ctx.fillText(imt_mean+' mm', Math.max(pa.x,pm.x)+8, (pa.y+pm.y)/2+8);*/
  602. }
  603. else {
  604. let pxwidth = this.media[this.currentMediaIndex].metrics.pxwidth;
  605. let pxheight = this.media[this.currentMediaIndex].metrics.pxheight;
  606. let pts=[{x:points[0].x*pxwidth, y:points[0].y*pxheight}, {x:points[1].x*pxwidth, y:points[1].y*pxheight}];
  607. let distance = global.improc.distance(pts[0], pts[1]);
  608. console.log(distance);
  609. for(let i=0; i<4; i++) {
  610. ctx.strokeStyle = distance<10?'#ff0000':'#00ff00';
  611. ctx.beginPath();
  612. ctx.moveTo(points[0].x, points[0].y);
  613. ctx.lineTo(points[1].x, points[1].y);
  614. ctx.stroke();
  615. ctx.fillStyle = '#FFA500';
  616. ctx.beginPath();
  617. ctx.arc(points[0].x, points[0].y, 3, 0, Math.PI * 2, true);
  618. ctx.fill();
  619. ctx.beginPath();
  620. ctx.arc(points[1].x, points[1].y, 3, 0, Math.PI * 2, true);
  621. ctx.fill();
  622. }
  623. }
  624. },
  625. drawPlaque: function(ctx, points, computation) {
  626. if(computation) {
  627. let lw = ctx.lineWidth;
  628. ctx.lineWidth = 2;
  629. for(let i=0; i<4; i++) {
  630. for(let p=0; p<computation.longeantPlaque.length-1; p++) {
  631. ctx.beginPath();
  632. ctx.moveTo(computation.longeantPlaque[p].x, computation.longeantPlaque[p].y);
  633. ctx.lineTo(computation.longeantPlaque[p+1].x, computation.longeantPlaque[p+1].y);
  634. ctx.strokeStyle = '#FFA500';
  635. ctx.stroke();
  636. }
  637. }
  638. ctx.lineWidth = lw;
  639. for(let i=0; i<4; i++) {
  640. for(let p=0; p<computation.ptsTrouvesFin.length-1; p++) {
  641. ctx.beginPath();
  642. ctx.moveTo(computation.ptsTrouvesFin[p].x, computation.ptsTrouvesFin[p].y);
  643. ctx.lineTo(computation.ptsTrouvesFin[p+1].x, computation.ptsTrouvesFin[p+1].y);
  644. ctx.strokeStyle = '#00ff00';
  645. ctx.stroke();
  646. }
  647. ctx.beginPath();
  648. ctx.moveTo(computation.longeantPlaque[0].x, computation.longeantPlaque[0].y);
  649. ctx.lineTo(computation.ptsTrouvesFin[0].x, computation.ptsTrouvesFin[0].y);
  650. ctx.strokeStyle = '#00ff00';
  651. ctx.stroke();
  652. ctx.beginPath();
  653. ctx.moveTo(computation.longeantPlaque[computation.longeantPlaque.length-1].x, computation.longeantPlaque[computation.longeantPlaque.length-1].y);
  654. ctx.lineTo(computation.ptsTrouvesFin[computation.ptsTrouvesFin.length-1].x, computation.ptsTrouvesFin[computation.ptsTrouvesFin.length-1].y);
  655. ctx.strokeStyle = '#00ff00';
  656. ctx.stroke();
  657. }
  658. }
  659. else {
  660. let sz = this.actionStatus=='plaque'?points.length-1:points.length-2;
  661. for(let i=0; i<4; i++) {
  662. for(let p=0; p<sz; p++) {
  663. ctx.beginPath();
  664. ctx.moveTo(points[p].x, points[p].y);
  665. ctx.lineTo(points[p+1].x, points[p+1].y);
  666. ctx.strokeStyle = '#00ff00';
  667. ctx.stroke();
  668. }
  669. }
  670. for(let i=0; i<4; i++) {
  671. for(let p=0; p<points.length-1; p++) {
  672. ctx.fillStyle = '#FFA500';
  673. ctx.beginPath();
  674. ctx.arc(points[p].x, points[p].y, 3, 0, Math.PI * 2, true);
  675. ctx.fill();
  676. ctx.beginPath();
  677. ctx.arc(points[p+1].x, points[p+1].y, 3, 0, Math.PI * 2, true);
  678. ctx.fill();
  679. }
  680. }
  681. }
  682. },
  683. drawCalibation: function(ctx, points, computation) {
  684. let isDicom = (this.media[this.currentMediaIndex].filename.indexOf('.dicom')!==-1);
  685. for(let i=0; i<4; i++) {
  686. ctx.beginPath();
  687. ctx.moveTo(points[0].x, points[0].y);
  688. ctx.lineTo(points[1].x, points[1].y);
  689. ctx.strokeStyle = isDicom?'#FFA500':'#00ff00';
  690. ctx.stroke();
  691. let step = 5/this.zoom;
  692. ctx.strokeStyle = '#FFA500';
  693. ctx.beginPath();
  694. ctx.moveTo(points[0].x-step, points[0].y);
  695. ctx.lineTo(points[0].x+step, points[0].y);
  696. ctx.stroke();
  697. ctx.beginPath();
  698. ctx.moveTo(points[1].x-step, points[1].y);
  699. ctx.lineTo(points[1].x+step, points[1].y);
  700. ctx.stroke();
  701. //ctx.beginPath();
  702. //ctx.arc(points[0].x, points[0].y, 3, 0, Math.PI * 2, true);
  703. //ctx.fill();
  704. //ctx.beginPath();
  705. //ctx.arc(points[1].x, points[1].y, 3, 0, Math.PI * 2, true);
  706. //ctx.fill();
  707. }
  708. if(computation) {
  709. let distance = Math.round(computation.distance);
  710. ctx.font = this.scaleIn(16)+'px sans-serif';
  711. ctx.textAlign = 'center';
  712. //ctx.strokeText(distance, points[0].x, points[0].y-18);
  713. ctx.fillStyle = isDicom?'#FFA500':'#00ff00';
  714. ctx.fillText(distance, points[0].x, points[0].y-18);
  715. }
  716. },
  717. drawDistance: function(ctx, points, computation) {
  718. for(let i=0; i<4; i++) {
  719. ctx.beginPath();
  720. ctx.moveTo(points[0].x, points[0].y);
  721. ctx.lineTo(points[1].x, points[1].y);
  722. ctx.strokeStyle = '#00ff00';
  723. ctx.stroke();
  724. ctx.fillStyle = '#FFA500';
  725. ctx.beginPath();
  726. ctx.arc(points[0].x, points[0].y, 3, 0, Math.PI * 2, true);
  727. ctx.fill();
  728. ctx.beginPath();
  729. ctx.arc(points[1].x, points[1].y, 3, 0, Math.PI * 2, true);
  730. ctx.fill();
  731. }
  732. let distance = 0;
  733. if(computation) {
  734. distance = Math.round(computation.distance*100)/100;
  735. }
  736. else {
  737. let pxwidth = this.media[this.currentMediaIndex].metrics.pxwidth;
  738. let pxheight = this.media[this.currentMediaIndex].metrics.pxheight;
  739. let pts=[{x:points[0].x*pxwidth, y:points[0].y*pxheight}, {x:points[1].x*pxwidth, y:points[1].y*pxheight}];
  740. distance = global.improc.distance(pts[0], pts[1]);
  741. distance = Math.round(distance*100)/100;
  742. }
  743. ctx.font = this.scaleIn(16)+'px sans-serif';
  744. ctx.textAlign = 'center';
  745. //ctx.strokeText(distance+' mm', points[0].x, points[0].y-18);
  746. ctx.fillStyle = "#00ff00";
  747. ctx.fillText(distance+' mm', points[0].x, points[0].y-18);
  748. },
  749. drawArea: function(ctx, points, computation) {
  750. for(let i=0; i<4; i++) {
  751. for(let p=0; p<points.length-1; p++) {
  752. ctx.beginPath();
  753. ctx.moveTo(points[p].x, points[p].y);
  754. ctx.lineTo(points[p+1].x, points[p+1].y);
  755. ctx.strokeStyle = '#00ff00';
  756. ctx.stroke();
  757. }
  758. }
  759. for(let i=0; i<4; i++) {
  760. ctx.beginPath();
  761. ctx.moveTo(points[points.length-1].x, points[points.length-1].y);
  762. ctx.lineTo(points[0].x, points[0].y);
  763. ctx.strokeStyle = '#00ff00';
  764. ctx.stroke();
  765. }
  766. for(let i=0; i<4; i++) {
  767. for(let p=0; p<points.length-1; p++) {
  768. ctx.fillStyle = '#FFA500';
  769. ctx.beginPath();
  770. ctx.arc(points[p].x, points[p].y, 3, 0, Math.PI * 2, true);
  771. ctx.fill();
  772. ctx.beginPath();
  773. ctx.arc(points[p+1].x, points[p+1].y, 3, 0, Math.PI * 2, true);
  774. ctx.fill();
  775. }
  776. }
  777. if(computation) {
  778. let surface = Math.round(computation.surface*100)/100;
  779. ctx.font = this.scaleIn(16)+'px sans-serif';
  780. ctx.textAlign = 'center';
  781. //ctx.strokeText(surface+' mm²', points[0].x, points[0].y-18);
  782. ctx.fillStyle = "#00ff00";
  783. ctx.fillText(surface+' mm²', points[0].x, points[0].y-18);
  784. }
  785. else if(points.length>=3) {
  786. let pxwidth = this.media[this.currentMediaIndex].metrics.pxwidth;
  787. let pxheight = this.media[this.currentMediaIndex].metrics.pxheight;
  788. let nbp = global.improc.surface(points, this.zoom);
  789. let surface = Math.round(nbp*pxwidth*pxheight*100)/100;
  790. ctx.font = this.scaleIn(16)+'px sans-serif';
  791. ctx.textAlign = 'center';
  792. //ctx.strokeText(surface+' mm²', points[0].x, points[0].y-18);
  793. ctx.fillStyle = "#00ff00";
  794. ctx.fillText(surface+' mm²', points[0].x, points[0].y-18);
  795. }
  796. },
  797. calibrationAction: function() {
  798. if(this.actionStatus=='calibration') {
  799. this.actionStatus=null;
  800. $$('#measure-calibration').removeClass('outlined');
  801. $$('#measure-imt').removeClass('outlined');
  802. $$('#measure-plaque').removeClass('outlined');
  803. $$('#measure-distance').removeClass('outlined');
  804. $$('#measure-area').removeClass('outlined');
  805. $$("body:hover").css({'cursor':'default'});
  806. }
  807. else if(this.actionStatus==null) {
  808. this.actionStatus='calibration';
  809. this.points = [];
  810. $$('#measure-calibration').addClass('outlined');
  811. $$("body:hover").css({'cursor':'url(\'static/images/cursor.png\') 8 8, auto'});
  812. }
  813. this.update();
  814. },
  815. imtAction: function() {
  816. // Specs changed later on here, so lets comment this for all measures, in case specs change back again
  817. //if(this.actionStatus=='imt') {
  818. this.actionStatus=null;
  819. $$('#measure-calibration').removeClass('outlined');
  820. $$('#measure-imt').removeClass('outlined');
  821. $$('#measure-plaque').removeClass('outlined');
  822. $$('#measure-distance').removeClass('outlined');
  823. $$('#measure-area').removeClass('outlined');
  824. $$("body:hover").css({'cursor':'default'});
  825. /*}
  826. else if(this.actionStatus==null) {*/
  827. this.actionStatus='imt';
  828. this.points = [];
  829. $$('#measure-imt').addClass('outlined');
  830. $$("body:hover").css({'cursor':'url(\'static/images/cursor.png\') 8 8, auto'});
  831. //}
  832. this.update();
  833. },
  834. plaqueAction: function() {
  835. //if(this.actionStatus=='plaque') {
  836. this.actionStatus=null;
  837. $$('#measure-calibration').removeClass('outlined');
  838. $$('#measure-imt').removeClass('outlined');
  839. $$('#measure-plaque').removeClass('outlined');
  840. $$('#measure-distance').removeClass('outlined');
  841. $$('#measure-area').removeClass('outlined');
  842. $$("body:hover").css({'cursor':'default'});
  843. /*}
  844. else if(this.actionStatus==null) {*/
  845. this.actionStatus='plaque';
  846. this.points = [];
  847. $$('#measure-plaque').addClass('outlined');
  848. $$("body:hover").css({'cursor':'url(\'static/images/cursor.png\') 8 8, auto'});
  849. //}
  850. this.update();
  851. },
  852. distanceAction: function() {
  853. //if(this.actionStatus=='distance') {
  854. this.actionStatus=null;
  855. $$('#measure-calibration').removeClass('outlined');
  856. $$('#measure-imt').removeClass('outlined');
  857. $$('#measure-plaque').removeClass('outlined');
  858. $$('#measure-distance').removeClass('outlined');
  859. $$('#measure-area').removeClass('outlined');
  860. $$("body:hover").css({'cursor':'default'});
  861. /*}
  862. else if(this.actionStatus==null) {*/
  863. this.actionStatus='distance';
  864. this.points = [];
  865. $$('#measure-distance').addClass('outlined');
  866. $$("body:hover").css({'cursor':'url(\'static/images/cursor.png\') 8 8, auto'});
  867. //}
  868. this.update();
  869. },
  870. areaAction: function() {
  871. //if(this.actionStatus=='area') {
  872. this.actionStatus=null;
  873. $$('#measure-calibration').removeClass('outlined');
  874. $$('#measure-imt').removeClass('outlined');
  875. $$('#measure-plaque').removeClass('outlined');
  876. $$('#measure-distance').removeClass('outlined');
  877. $$('#measure-area').removeClass('outlined');
  878. $$("body:hover").css({'cursor':'default'});
  879. /*}
  880. else if(this.actionStatus==null) {*/
  881. this.actionStatus='area';
  882. this.points = [];
  883. $$('#measure-area').addClass('outlined');
  884. $$("body:hover").css({'cursor':'url(\'static/images/cursor.png\') 8 8, auto'});
  885. //}
  886. this.update();
  887. },
  888. savePlaque: function(keepPlaques) {
  889. console.log('keepPlaques', keepPlaques);
  890. let that = this;
  891. let postData = {
  892. visitID: app.data.patient.visitID,
  893. filename: that.media[that.currentMediaIndex].filename,
  894. points: that.points,
  895. pxwidth: that.media[that.currentMediaIndex].metrics.pxwidth,
  896. pxheight: that.media[that.currentMediaIndex].metrics.pxheight,
  897. frame: (that.videoViewer?that.videoViewer.currentFrame:0),
  898. keepPlaques: keepPlaques,
  899. apiKey: ''
  900. };
  901. // save plaque
  902. app.preloader.show();
  903. app.request.post(app.data.config.apiBaseURL + '/measure/plaque/', postData, function (data) {
  904. console.log('measure/plaque', data);
  905. app.preloader.hide();
  906. if (data.result == 'ERROR') {
  907. switch (data.reason) {
  908. case 'denied':
  909. app.methods.signout(global.tr[global.lang].topLevel.warning.disconnected);
  910. break;
  911. case 'algo_failed':
  912. app.dialog.alert(global.tr[global.lang].topLevel.error.algo_failed);
  913. break;
  914. case 'no_credit':
  915. app.dialog.alert(global.tr[global.lang].topLevel.error.no_credit);
  916. break;
  917. default:
  918. app.dialog.alert(global.tr[global.lang].topLevel.error.internal_error);
  919. break;
  920. }
  921. }
  922. else {
  923. // update credits
  924. $$('#credit_available').text(data.credit_left);
  925. // add measure
  926. that.media[that.currentMediaIndex].measure = data.measures;
  927. that.addDicomCalibration(that.currentMediaIndex);
  928. that.updateMeasureList();
  929. //
  930. that.updateButtonStates();
  931. }
  932. // update
  933. that.actionStatus=null;
  934. that.update();
  935. }, function (data) {
  936. console.log('error', data);
  937. }, 'json');
  938. },
  939. mousedown: function(e) {
  940. if(this.media[this.currentMediaIndex].filename.indexOf('.dicom')!==-1) {
  941. let x = this.scaleIn(e.offsetX);
  942. let y = this.scaleIn(e.offsetY);
  943. for (let i=0; i<this.media[this.currentMediaIndex].measure.length; ++i) {
  944. let M = this.media[this.currentMediaIndex].measure[i];
  945. if(M.type == 'calibration') {
  946. for (let k=0; k<M.points.length; ++k) {
  947. let distance = global.improc.distance({x:x, y:y}, M.points[k]);
  948. if(distance<=5) {
  949. this.calibrationHover = {m:i, p:k};
  950. return;
  951. }
  952. }
  953. return;
  954. }
  955. }
  956. }
  957. },
  958. mouseup: function(e) {
  959. this.calibrationHover = null;
  960. let that = this;
  961. if(this.actionStatus=='calibration') {
  962. if(this.points.length==0) {
  963. this.points.push({
  964. x: this.scaleIn(e.offsetX),
  965. y: this.scaleIn(e.offsetY)
  966. });
  967. this.points.push({
  968. x: this.scaleIn(e.offsetX),
  969. y: this.scaleIn(e.offsetY)
  970. });
  971. this.update();
  972. }
  973. else if(this.points.length==2) {
  974. $$('#measure-calibration').removeClass('outlined');
  975. $$("body:hover").css({'cursor':'default'});
  976. this.points[1] = {
  977. x: this.points[1].x,
  978. y: this.scaleIn(e.offsetY)
  979. };
  980. // auto detect scale
  981. let scale = global.improc.computeScale(this.getImageData(), this.zoom, this.points[0], this.points[1]);
  982. console.log('computeScale', scale);
  983. if(scale.rep>=2) {
  984. this.points[0] = scale.p1;
  985. this.points[1] = scale.p2;
  986. this.actionStatus='calibrationConfirm';
  987. }
  988. else {
  989. this.actionStatus=null;
  990. }
  991. this.update();
  992. // confirm
  993. if(this.actionStatus=='calibrationConfirm') {
  994. var dlg = app.dialog.prompt(global.tr[global.lang].measure.calibrationLength, function(scale) {
  995. console.log('scale', scale);
  996. scale = parseFloat(scale);
  997. let distance = global.improc.distance(that.points[0], that.points[1]);
  998. console.log('distance', distance);
  999. console.log(scale/distance);
  1000. if(scale == '' || scale === NaN || parseInt(scale) <= 0) {
  1001. app.dialog.alert(global.tr[global.lang].measure.invalidDistance);
  1002. // update
  1003. that.actionStatus=null;
  1004. that.update();
  1005. }
  1006. else if(!Number.isInteger(parseFloat(scale))) {
  1007. app.dialog.alert(global.tr[global.lang].measure.integerPlease);
  1008. // update
  1009. that.actionStatus=null;
  1010. that.update();
  1011. }
  1012. else if(parseInt(scale)<10 || parseInt(scale)>95) {
  1013. app.dialog.alert(global.tr[global.lang].measure.invalidRange);
  1014. // update
  1015. that.actionStatus=null;
  1016. that.update();
  1017. }
  1018. else {
  1019. // save calibration
  1020. let postData = {
  1021. visitID: app.data.patient.visitID,
  1022. filename: that.media[that.currentMediaIndex].filename,
  1023. scale: scale/distance,
  1024. computation: {
  1025. distance: scale
  1026. },
  1027. points: that.points,
  1028. frame: (that.videoViewer?that.videoViewer.currentFrame:0),
  1029. apiKey: ''
  1030. };
  1031. app.preloader.show();
  1032. app.request.post(app.data.config.apiBaseURL + '/measure/calibration/', postData, function (data) {
  1033. console.log('measure/calibration', data);
  1034. app.preloader.hide();
  1035. if (data.result == 'ERROR') {
  1036. switch (data.reason) {
  1037. case 'denied':
  1038. app.methods.signout(global.tr[global.lang].topLevel.warning.disconnected);
  1039. break;
  1040. default:
  1041. app.dialog.alert(global.tr[global.lang].topLevel.error.internal_error);
  1042. break;
  1043. }
  1044. }
  1045. else {
  1046. console.log(scale/distance);
  1047. that.media[that.currentMediaIndex].metrics.pxwidth=scale/distance;
  1048. that.media[that.currentMediaIndex].metrics.pxheight=scale/distance;
  1049. // remove existing calibration
  1050. for(let i=0; i<that.media[that.currentMediaIndex].measure.length; ++i) {
  1051. if(that.media[that.currentMediaIndex].measure[i].type == 'calibration') {
  1052. that.media[that.currentMediaIndex].measure.splice(i, 1);
  1053. break;
  1054. }
  1055. }
  1056. // add measure
  1057. that.media[that.currentMediaIndex].measure.push(data.measure);
  1058. that.updateMeasureList();
  1059. //
  1060. that.updateButtonStates();
  1061. that.mouseout();
  1062. }
  1063. // update
  1064. that.actionStatus=null;
  1065. that.update();
  1066. }, function (data) {
  1067. console.log('error', data);
  1068. }, 'json');
  1069. }
  1070. }, function() {
  1071. // update on cancel
  1072. that.actionStatus=null;
  1073. that.update();
  1074. }, '');
  1075. console.log(dlg);
  1076. dlg.params.cssClass = 'prompt-on-top';
  1077. dlg.$el.css({
  1078. 'top':(dlg.$el.height()/2)+'px'
  1079. });
  1080. /*dlg.on('opened', function() {
  1081. $$(".dialog").css({
  1082. 'top':($$(".dialog").height()/2)+'px'
  1083. });
  1084. });*/
  1085. }
  1086. }
  1087. }
  1088. else if(this.actionStatus=='distance') {
  1089. if(this.points.length==0) {
  1090. this.points.push({
  1091. x: this.scaleIn(e.offsetX),
  1092. y: this.scaleIn(e.offsetY)
  1093. });
  1094. this.points.push({
  1095. x: this.scaleIn(e.offsetX),
  1096. y: this.scaleIn(e.offsetY)
  1097. });
  1098. this.update();
  1099. }
  1100. else if(this.points.length==2) {
  1101. $$('#measure-distance').removeClass('outlined');
  1102. $$("body:hover").css({'cursor':'default'});
  1103. this.points[1] = {
  1104. x: this.scaleIn(e.offsetX),
  1105. y: this.scaleIn(e.offsetY)
  1106. };
  1107. // distance type
  1108. app.dialog.create({
  1109. title: global.tr[global.lang].measure.distanceType,
  1110. text: null,
  1111. buttons: [{
  1112. text: global.tr[global.lang].measure.thickness,
  1113. },{
  1114. text: global.tr[global.lang].measure.diameter,
  1115. },{
  1116. text: global.tr[global.lang].measure.plaqueThickness,
  1117. },{
  1118. text: global.tr[global.lang].topLevel.cancel,
  1119. },
  1120. ],
  1121. onClick: function (dialog, index) {
  1122. let pxwidth = that.media[that.currentMediaIndex].metrics.pxwidth;
  1123. let pxheight = that.media[that.currentMediaIndex].metrics.pxheight;
  1124. let pts=[{x:that.points[0].x*pxwidth, y:that.points[0].y*pxheight}, {x:that.points[1].x*pxwidth, y:that.points[1].y*pxheight}];
  1125. let distance = global.improc.distance(pts[0], pts[1]);
  1126. let postData = {
  1127. visitID: app.data.patient.visitID,
  1128. filename: that.media[that.currentMediaIndex].filename,
  1129. points: that.points,
  1130. computation: {
  1131. distance: distance
  1132. },
  1133. frame: (that.videoViewer?that.videoViewer.currentFrame:0),
  1134. type: '',
  1135. apiKey: ''
  1136. };
  1137. if(index === 0){
  1138. postData.type='thickness';
  1139. }
  1140. else if(index === 1){
  1141. postData.type='diameter';
  1142. }
  1143. else if(index === 2){
  1144. postData.type='plaque_thickness';
  1145. }
  1146. else {
  1147. // update
  1148. that.actionStatus=null;
  1149. that.update();
  1150. return;
  1151. }
  1152. // save distance
  1153. app.preloader.show();
  1154. app.request.post(app.data.config.apiBaseURL + '/measure/distance/', postData, function (data) {
  1155. console.log('measure/distance', data);
  1156. app.preloader.hide();
  1157. if (data.result == 'ERROR') {
  1158. switch (data.reason) {
  1159. case 'denied':
  1160. app.methods.signout(global.tr[global.lang].topLevel.warning.disconnected);
  1161. break;
  1162. default:
  1163. app.dialog.alert(global.tr[global.lang].topLevel.error.internal_error);
  1164. break;
  1165. }
  1166. }
  1167. else {
  1168. // add measure
  1169. that.media[that.currentMediaIndex].measure = data.measures;
  1170. that.addDicomCalibration(that.currentMediaIndex);
  1171. that.updateMeasureList();
  1172. //
  1173. that.updateButtonStates();
  1174. }
  1175. // update
  1176. that.actionStatus=null;
  1177. that.update();
  1178. }, function (data) {
  1179. console.log('error', data);
  1180. }, 'json');
  1181. },
  1182. verticalButtons: true,
  1183. }).open();
  1184. }
  1185. }
  1186. else if(this.actionStatus=='area') {
  1187. if(this.points.length==0) {
  1188. this.points.push({
  1189. x: this.scaleIn(e.offsetX),
  1190. y: this.scaleIn(e.offsetY)
  1191. });
  1192. this.points.push({
  1193. x: this.scaleIn(e.offsetX),
  1194. y: this.scaleIn(e.offsetY)
  1195. });
  1196. this.update();
  1197. }
  1198. else {
  1199. this.points.push({
  1200. x: this.scaleIn(e.offsetX),
  1201. y: this.scaleIn(e.offsetY)
  1202. });
  1203. this.update();
  1204. }
  1205. }
  1206. else if(this.actionStatus=='imt') {
  1207. if(this.points.length==0) {
  1208. this.points.push({
  1209. x: this.scaleIn(e.offsetX),
  1210. y: this.scaleIn(e.offsetY)
  1211. });
  1212. this.points.push({
  1213. x: this.scaleIn(e.offsetX),
  1214. y: this.scaleIn(e.offsetY)
  1215. });
  1216. this.update();
  1217. }
  1218. else if(this.points.length==2) {
  1219. $$('#measure-imt').removeClass('outlined');
  1220. $$("body:hover").css({'cursor':'default'});
  1221. this.points[1] = {
  1222. x: this.scaleIn(e.offsetX),
  1223. y: this.scaleIn(e.offsetY)
  1224. };
  1225. let that = this;
  1226. let postData = {
  1227. visitID: app.data.patient.visitID,
  1228. filename: this.media[this.currentMediaIndex].filename,
  1229. points: this.points,
  1230. pxwidth: this.media[this.currentMediaIndex].metrics.pxwidth,
  1231. pxheight: this.media[this.currentMediaIndex].metrics.pxheight,
  1232. frame: (that.videoViewer?that.videoViewer.currentFrame:0),
  1233. apiKey: ''
  1234. };
  1235. // save imt
  1236. app.preloader.show();
  1237. app.request.post(app.data.config.apiBaseURL + '/measure/imt/', postData, function (data) {
  1238. console.log('measure/imt', data);
  1239. app.preloader.hide();
  1240. if (data.result == 'ERROR') {
  1241. switch (data.reason) {
  1242. case 'denied':
  1243. app.methods.signout(global.tr[global.lang].topLevel.warning.disconnected);
  1244. break;
  1245. case 'no_credit':
  1246. app.dialog.alert(global.tr[global.lang].topLevel.error.no_credit);
  1247. break;
  1248. default:
  1249. app.dialog.alert(global.tr[global.lang].topLevel.error.internal_error);
  1250. break;
  1251. }
  1252. }
  1253. else {
  1254. // update credits
  1255. $$('#credit_available').text(data.credit_left);
  1256. // add measure
  1257. that.media[that.currentMediaIndex].measure = data.measures;
  1258. that.addDicomCalibration(that.currentMediaIndex);
  1259. that.updateMeasureList();
  1260. //
  1261. that.updateButtonStates();
  1262. }
  1263. // update
  1264. that.actionStatus=null;
  1265. that.update();
  1266. }, function (data) {
  1267. console.log('error', data);
  1268. }, 'json');
  1269. // JS version
  1270. //let res = improc.eim(this.getImageData(), this.points[0], this.points[1]);
  1271. //console.log(res);
  1272. }
  1273. }
  1274. else if(this.actionStatus=='plaque') {
  1275. console.log('enter add point', this.points, this.points.length);
  1276. if(this.points.length==0) {
  1277. console.log('add 2');
  1278. this.points.push({
  1279. x: this.scaleIn(e.offsetX),
  1280. y: this.scaleIn(e.offsetY)
  1281. });
  1282. this.points.push({
  1283. x: this.scaleIn(e.offsetX),
  1284. y: this.scaleIn(e.offsetY)
  1285. });
  1286. this.update();
  1287. }
  1288. else {
  1289. this.points.push({
  1290. x: this.scaleIn(e.offsetX),
  1291. y: this.scaleIn(e.offsetY)
  1292. });
  1293. this.update();
  1294. }
  1295. console.log('leave add point', this.points, this.points.length);
  1296. }
  1297. else if(this.actionStatus=='plaque2') {
  1298. $$('#measure-plaque').removeClass('outlined');
  1299. $$("body:hover").css({'cursor':'default'});
  1300. this.points[this.points.length-1] = {
  1301. x: this.scaleIn(e.offsetX),
  1302. y: this.scaleIn(e.offsetY)
  1303. };
  1304. this.points.splice(this.points.length-2, 1);
  1305. console.log('plaque points', this.points);
  1306. let that = this;
  1307. let hasPlaqueAlready = false;
  1308. for (let i=0; i<this.media[this.currentMediaIndex].measure.length; i++) {
  1309. if(this.media[this.currentMediaIndex].measure[i].type=='plaque') {
  1310. hasPlaqueAlready = true;
  1311. break;
  1312. }
  1313. }
  1314. if(hasPlaqueAlready) {
  1315. app.dialog.create({
  1316. title: global.tr[global.lang].measure.plaqueNew,
  1317. text: null,
  1318. buttons: [{
  1319. text: global.tr[global.lang].measure.plaqueNewYes,
  1320. },{
  1321. text: global.tr[global.lang].measure.plaqueNewNo,
  1322. },{
  1323. text: global.tr[global.lang].topLevel.cancel,
  1324. },
  1325. ],
  1326. onClick: function (dialog, index) {
  1327. let keepPlaques = false;
  1328. if(index === 0){
  1329. keepPlaques=true;
  1330. }
  1331. else if(index === 1){
  1332. }
  1333. else {
  1334. // update
  1335. that.actionStatus=null;
  1336. that.update();
  1337. return;
  1338. }
  1339. that.savePlaque(keepPlaques);
  1340. },
  1341. verticalButtons: true,
  1342. }).open();
  1343. }
  1344. else {
  1345. this.savePlaque(false);
  1346. }
  1347. }
  1348. else if(this.calibrationHover!==null) {
  1349. this.calibrationHover = null;
  1350. $$("body:hover").css({'cursor':'default'});
  1351. }
  1352. },
  1353. mouseout: function() {
  1354. let mx = this.media[this.currentMediaIndex].metrics.pxwidth.toFixed(2);
  1355. let my = this.media[this.currentMediaIndex].metrics.pxheight.toFixed(2);
  1356. let scale = mx+'x'+my;
  1357. if(mx == 0 || my == 0) {
  1358. scale = '<span style="color:red;">'+scale+'</span>';
  1359. }
  1360. $$('#mouseCoord').html('['+scale+']');
  1361. },
  1362. mousemove: function(e) {
  1363. let mx = this.media[this.currentMediaIndex].metrics.pxwidth.toFixed(2);
  1364. let my = this.media[this.currentMediaIndex].metrics.pxheight.toFixed(2);
  1365. let scale = mx+'x'+my;
  1366. if(mx == 0 || my == 0) {
  1367. scale = '<span style="color:red;">'+scale+'</span>';
  1368. }
  1369. $$('#mouseCoord').html('('+this.scaleIn(e.offsetX)+', '+this.scaleIn(e.offsetY)+')'+' '+'['+scale+']');
  1370. if(this.actionStatus=='calibration' && this.points.length==2) {
  1371. this.points[1] = {
  1372. x: this.points[0].x,
  1373. y: this.scaleIn(e.offsetY)
  1374. };
  1375. this.update();
  1376. }
  1377. else if(this.actionStatus=='distance' && this.points.length==2) {
  1378. this.points[1] = {
  1379. x: this.scaleIn(e.offsetX),
  1380. y: this.scaleIn(e.offsetY)
  1381. };
  1382. this.update();
  1383. }
  1384. else if(this.actionStatus=='area' && this.points.length>=2) {
  1385. this.points[this.points.length-1] = {
  1386. x: this.scaleIn(e.offsetX),
  1387. y: this.scaleIn(e.offsetY)
  1388. };
  1389. this.update();
  1390. }
  1391. else if(this.actionStatus=='imt' && this.points.length>=2) {
  1392. this.points[1] = {
  1393. x: this.scaleIn(e.offsetX),
  1394. y: this.scaleIn(e.offsetY)
  1395. };
  1396. this.update();
  1397. }
  1398. else if(this.actionStatus=='plaque' && this.points.length>=2) {
  1399. this.points[this.points.length-1] = {
  1400. x: this.scaleIn(e.offsetX),
  1401. y: this.scaleIn(e.offsetY)
  1402. };
  1403. this.update();
  1404. }
  1405. else if(this.calibrationHover!==null) {
  1406. //console.log('calibrationHover', this.calibrationHover);
  1407. let points = this.media[this.currentMediaIndex].measure[this.calibrationHover.m].points;
  1408. let P = points[this.calibrationHover.p];
  1409. let dx = this.scaleIn(e.offsetX) - P.x;
  1410. let dy = this.scaleIn(e.offsetY) - P.y;
  1411. this.media[this.currentMediaIndex].measure[this.calibrationHover.m].points[0].x += dx;
  1412. this.media[this.currentMediaIndex].measure[this.calibrationHover.m].points[0].y += dy;
  1413. this.media[this.currentMediaIndex].measure[this.calibrationHover.m].points[1].x += dx;
  1414. this.media[this.currentMediaIndex].measure[this.calibrationHover.m].points[1].y += dy;
  1415. this.update();
  1416. }
  1417. else if(this.media[this.currentMediaIndex].filename.indexOf('.dicom')!==-1) {
  1418. let x = this.scaleIn(e.offsetX);
  1419. let y = this.scaleIn(e.offsetY);
  1420. for (let i=0; i<this.media[this.currentMediaIndex].measure.length; ++i) {
  1421. let M = this.media[this.currentMediaIndex].measure[i];
  1422. if(M.type == 'calibration') {
  1423. for (let k=0; k<M.points.length; ++k) {
  1424. let distance = global.improc.distance({x:x, y:y}, M.points[k]);
  1425. if(distance<=5) {
  1426. //$$("body:hover").css({'cursor':'move'});
  1427. return;
  1428. }
  1429. }
  1430. //$$("body:hover").css({'cursor':'default'});
  1431. return;
  1432. }
  1433. }
  1434. }
  1435. },
  1436. dblclick: function(e) {
  1437. let that = this;
  1438. if(this.actionStatus=='area' && this.points.length>=2) {
  1439. $$('#measure-area').removeClass('outlined');
  1440. $$("body:hover").css({'cursor':'default'});
  1441. this.points[this.points.length-1] = {
  1442. x: this.scaleIn(e.offsetX),
  1443. y: this.scaleIn(e.offsetY)
  1444. };
  1445. let pxwidth = this.media[this.currentMediaIndex].metrics.pxwidth;
  1446. let pxheight = this.media[this.currentMediaIndex].metrics.pxheight;
  1447. let nbp = global.improc.surface(this.points, 1.0);
  1448. let postData = {
  1449. visitID: app.data.patient.visitID,
  1450. filename: that.media[that.currentMediaIndex].filename,
  1451. points: that.points,
  1452. computation: {
  1453. surface: nbp*pxwidth*pxheight
  1454. },
  1455. frame: (that.videoViewer?that.videoViewer.currentFrame:0),
  1456. apiKey: ''
  1457. };
  1458. // save area
  1459. app.preloader.show();
  1460. app.request.post(app.data.config.apiBaseURL + '/measure/area/', postData, function (data) {
  1461. console.log('measure/area', data);
  1462. app.preloader.hide();
  1463. if (data.result == 'ERROR') {
  1464. switch (data.reason) {
  1465. case 'denied':
  1466. app.methods.signout(global.tr[global.lang].topLevel.warning.disconnected);
  1467. break;
  1468. default:
  1469. app.dialog.alert(global.tr[global.lang].topLevel.error.internal_error);
  1470. break;
  1471. }
  1472. }
  1473. else {
  1474. // add measure
  1475. that.media[that.currentMediaIndex].measure = data.measures;
  1476. that.addDicomCalibration(that.currentMediaIndex);
  1477. that.updateMeasureList();
  1478. //
  1479. that.updateButtonStates();
  1480. }
  1481. // update
  1482. that.actionStatus=null;
  1483. that.update();
  1484. }, function (data) {
  1485. console.log('error', data);
  1486. }, 'json');
  1487. }
  1488. else if(this.actionStatus=='plaque' && this.points.length>=2) {
  1489. console.log("change cursor now");
  1490. this.actionStatus='plaque2';
  1491. $$("body:hover").css({'cursor':'default'});
  1492. $$("body:hover").css({'cursor':'url(\'static/images/cursor_round.png\') 8 8, auto'});
  1493. this.update();
  1494. }
  1495. }
  1496. }
  1497. }
  1498. </script>