// jshint esversion: 6 let gui = new lil.GUI(); let hideTimeout = false; let svg; let zoom = 1; let oldZoom = 1; let panZoomPanther; let foundAnimate = 1; let foundScale = 1.05; let myPanZoomTimer = null; let historytime = + new Date(); let fel1 = false; let fel2 = false; const panZoomTime = 500; const facilities = ['']; const names = []; const shrinkedNames = []; const serviceareanames = []; const alias = []; let maxZoom = 230; const status = []; const compData = {}; const point = {x: 0, y: 0}; const pa = document.location.search.replace('?','').split('&'); const parameters = {}; const state = document.location.search.indexOf('ps')>-1; const vlv = document.location.search.indexOf('vlv')>-1; const vlvs = []; const speechToken = Math.floor(Math.random() * 1000000000000); for (let i=0; i<pa.length; i++) {const p = pa[i].split('='); parameters[p[0]] = p[1];} const machineCaseSensitive = document.location.search.indexOf('machine=')>-1? document.location.search.split('machine=')[1].split('&')[0]: 'elettra'; const machine = machineCaseSensitive.toLowerCase(); let measurementStart = false; let cx, cy; let measurementType = document.location.search.indexOf('measurement=')>-1? document.location.search.split('measurement=')[1].split('&')[0].split(','): document.location.search.indexOf('measurement')>-1; let measurementEvent = 'contextmenu'; if (typeof measurementType == "object") { const index = measurementType.indexOf('left'); if (index>-1) { measurementEvent = 'mousedown'; measurementType.splice(index, 1); } if (measurementType.length==0) $('#bm').hide(); } else $('#bm').hide(); let measurement = typeof measurementType == "object" || measurementType; function setTick(x, y, cx, cy, dist, distPixel, n, pow10=1000) { const tx1 = cx + (x - cx) / dist * pow10 * n; const ty1 = cy + (y - cy) / dist * pow10 * n; const tx2 = tx1 - (y - cy) / distPixel * 10; const ty2 = ty1 + (x - cx) / distPixel * 10; $('#tick'+n).attr('x1', tx1); $('#tick'+n).attr('y1', ty1); $('#tick'+n).attr('x2', tx2); $('#tick'+n).attr('y2', ty2); $('#tickLabel'+n).attr('x', 2*tx2-tx1); $('#tickLabel'+n).attr('y', 2*ty2-ty1); $('#tickLabel'+n).html(x==cx || distPixel<220? '': pow10 * n / 1000); } // https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment function pDistance(x, y, x1, y1, x2, y2) { var A = x - x1; var B = y - y1; var C = x2 - x1; var D = y2 - y1; var dot = A * C + B * D; var len_sq = C * C + D * D; var param = -1; if (len_sq != 0) param = dot / len_sq; //in case of 0 length line var xx, yy; if (param < 0) { xx = x1; yy = y1; } else if (param > 1) { xx = x2; yy = y2; } else { xx = x1 + param * C; yy = y1 + param * D; } var dx = x - xx; var dy = y - yy; return {dist: Math.sqrt(dx * dx + dy * dy), x: xx, y: yy}; } function getCursorPosition(main, event) { const rect = main.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const distPixel = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2)); const dist = distPixel / panZoomPanther.getSizes().realZoom; if (measurementStart) $('#stright').val(Math.round(dist)/1000); // const alpha = (Math.atan2(y-cy, x-cx) * 180) / Math.PI; $('#pos').val(Math.round((x - panZoomPanther.getPan().x)/panZoomPanther.getSizes().realZoom)/1000+' '+(-Math.round((y - panZoomPanther.getPan().y)/panZoomPanther.getSizes().realZoom)/1000)); if (measurementStart) { // stright measurement const pow10 = Math.pow(10, Math.round(Math.log10(dist))-1); $('#measurementLine').attr('x2', x); $('#measurementLine').attr('y2', y); $('#measurementLineStroke').attr('x2', x); $('#measurementLineStroke').attr('y2', y); for (let i=1; i<32; i++) { if (i<dist/pow10 && distPixel>50) setTick(x, y, cx, cy, dist, distPixel, i, pow10); else setTick(x, y, x, y, dist, distPixel, i, pow10); } const vertex = []; let minDistance = Infinity; let dindex = -1; let mincDistance = Infinity; let dcindex = -1; // beam pattern measurement if (typeof measurementType == "object" && typeof lattice[measurementType[0]] == "object") { let d = 0; const x1 = (x - panZoomPanther.getPan().x) / panZoomPanther.getSizes().realZoom; const y1 = (y - panZoomPanther.getPan().y) / panZoomPanther.getSizes().realZoom; const xc1 = (cx - panZoomPanther.getPan().x) / panZoomPanther.getSizes().realZoom; const yc1 = (cy - panZoomPanther.getPan().y) / panZoomPanther.getSizes().realZoom; for (let i=0; i < lattice[measurementType[0]].sections.length; i++) { j = (i+1) % lattice[measurementType[0]].sections.length; vertex[i] = lattice[measurementType[0]].sections[i].start; // d = Math.sqrt(Math.pow(vertex[i].x - x, 2) + Math.pow(vertex[i].z - y, 2)); d = pDistance(x1, y1, vertex[i].x, vertex[i].z, lattice[measurementType[0]].sections[j].start.x, lattice[measurementType[0]].sections[j].start.z); if (minDistance > d.dist) { minDistance = d.dist; dindex = i; p = d;} d = pDistance(xc1, yc1, vertex[i].x, vertex[i].z, lattice[measurementType[0]].sections[j].start.x, lattice[measurementType[0]].sections[j].start.z); if (mincDistance > d.dist) { mincDistance = d.dist; dcindex = i; pc = d;} } let pathd = 'M '+(pc.x*panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x)+' '+(pc.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y)+' '; k = dcindex; j = (k+1) % lattice[measurementType[0]].sections.length; let dd = Math.sqrt(Math.pow(pc.x-lattice[measurementType[0]].sections[j].start.x, 2) + Math.pow(pc.y-lattice[measurementType[0]].sections[j].start.z, 2)); if (dcindex == dindex) dd = Math.sqrt(Math.pow(pc.x-p.x, 2) + Math.pow(pc.y-p.y, 2)); else { while (k != dindex) { j = (k+1) % lattice[measurementType[0]].sections.length; if (k != dcindex) dd = dd + Math.sqrt(Math.pow(lattice[measurementType[0]].sections[k].start.x-lattice[measurementType[0]].sections[j].start.x, 2) + Math.pow(lattice[measurementType[0]].sections[k].start.z-lattice[measurementType[0]].sections[j].start.z, 2)); pathd = pathd + 'L '+(lattice[measurementType[0]].sections[j].start.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x)+' '+(lattice[measurementType[0]].sections[j].start.z * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y)+' '; k = j; } dd = dd + Math.sqrt(Math.pow(p.x-lattice[measurementType[0]].sections[dindex].start.x, 2) + Math.pow(p.y-lattice[measurementType[0]].sections[dindex].start.z, 2)); } $('#bm').val(Math.round(dd)/1000); pathd = pathd + 'L '+(p.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x)+' '+(p.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y); //<path d="M 10 10 H 90 V 90 H 10 L 10 10"/> console.log('minDistance', mincDistance, dcindex, pc, minDistance, dindex, p, dd, 'pathd', pathd); $('#beam').attr('d', pathd); $('#measurementSide1').attr('x2', pc.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x); $('#measurementSide1').attr('y2', pc.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y); $('#measurementSide2').attr('x1', x); $('#measurementSide2').attr('y1', y); $('#measurementSide2').attr('x2', p.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x); $('#measurementSide2').attr('y2', p.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y); } } } function clearmeasurement() { $('#measurementLine').attr('x1', cx); $('#measurementLine').attr('y1', cy); $('#measurementLine').attr('x2', cx); $('#measurementLine').attr('y2', cy); $('#measurementLineStroke').attr('x1', cx); $('#measurementLineStroke').attr('y1', cy); $('#measurementLineStroke').attr('x2', cx); $('#measurementLineStroke').attr('y2', cy); $('#measurementSide1').attr('x1', cx); $('#measurementSide1').attr('y1', cy); $('#measurementSide1').attr('x2', cx); $('#measurementSide1').attr('y2', cy); $('#measurementSide2').attr('x1', cx); $('#measurementSide2').attr('y1', cy); $('#measurementSide2').attr('x2', cx); $('#measurementSide2').attr('y2', cy); $('#stright').val(''); $('#bm').val(''); $('#beam').attr('d', ''); for (let i=1; i<32; i++) {setTick(cx, cy, cx, cy, 1, 1, i);} } function initCursorPosition(main, event) { if (measurementStart) { measurementStart = false; return;} const rect = main.getBoundingClientRect(); cx = event.clientX - rect.left; cy = event.clientY - rect.top; console.log(Math.round(cx) + "," + Math.round(cy) + ","); if (!measurementStart) { // appendSvg("circle", {id: 'measurementStart', cx: cx, cy: cy, r:8, style:"display: block", fill:"none", stroke:"red","stroke-width":2}); if ($('#measurementLine').length==0) { appendSvg("line", {id: 'measurementLineStroke', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"yellow", stroke:"black","stroke-width":4}); appendSvg("line", {id: 'measurementLine', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"yellow", stroke:"yellow","stroke-width":2}); appendSvg("path", {d: '', fill: "none", stroke: "limegreen", "stroke-width": 3, id:"beam"}); appendSvg("line", {id: 'measurementSide1', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"pink","stroke-width":2}); appendSvg("line", {id: 'measurementSide2', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"pink","stroke-width":2}); for (let i=1; i<32; i++) { appendSvg("line", {id: 'tick'+i, x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"yellow", stroke:"black","stroke-width":1}); appendSvg("text", {id: 'tickLabel'+i, x: cx, y: cy, fill:"white", stroke:"black","stroke-width":1, "font-family":"Arial", "font-size":20, "font-weight":"bold", "text-anchor": "start"}); } } else { clearmeasurement(); } measurementStart = true; } else { // $('#measurementStart').attr('cx', cx); $('#measurementStart').attr('cy', cy); $('#measurementLine').attr('x1', cx); $('#measurementLine').attr('y1', cy); $('#measurementLineStroke').attr('x1', cx); $('#measurementLineStroke').attr('y1', cy); } main.addEventListener('mousemove', function(e) { getCursorPosition(main, e); }); } if (document.location.search.indexOf('measurement')>-1) { measurementEnable(); } function measurementToggle(enable) { for (let i=1; i<100; i++) { if ($('#lil-gui-name-'+i).html()=='measurement') { if (enable) { $('#lil-gui-name-'+(i+1)).parent().show(); $('#lil-gui-name-'+(i+2)).parent().show(); } else { $('#lil-gui-name-'+(i+1)).parent().hide(); $('#lil-gui-name-'+(i+2)).parent().hide(); } break; } } } function measurementDisable() { clearmeasurement(); main.removeEventListener(measurementEvent, measurementListener, false); main.style.cursor = 'default'; measurementToggle(false); } function measurementListener(e) { e.preventDefault(); initCursorPosition(main, e); return false; } function measurementEnable() { $('#measurement').show(); const main = document.getElementById('main'); main.style.cursor = 'crosshair'; main.addEventListener(measurementEvent, measurementListener, false); measurementToggle(true); } function measurementFacility(msg) { console.log('measurementFacility()', msg); if (msg=='left') { if (measurementEvent != 'mousedown') { measurementDisable(); measurementEvent = 'mousedown'; measurementEnable(); } } else if (msg=='right') { if (measurementEvent == 'mousedown') { measurementDisable(); measurementEvent = 'contextmenu'; measurementEnable(); } } else if (msg=='') { measurementDisable(); measurementType = true; measurementEnable(); $('#bm').hide(); } else { measurementDisable(); measurementType = [msg]; measurementEnable(); $('#bm').show(); } } //https://puma-01.elettra.eu/rchan.php?json&valueOnly&src=srv-tango-srf-01.fcs.elettra.trieste.it:20000/f/access_control/safety/Undulator_access_state -> 5 FEL1, 6 FEL2 if (machine.indexOf('fermi')>-1) { fetch(conf.rchan+'srv-tango-srf-01:20000/f/access_control/safety/Undulator_access_state').then((response) => {return response.json();}).then((fel) => { fel1 = fel[5] == 1; fel2 = fel[6] == 1; }); } function resizeIframe(obj) { obj.style.height = obj.contentWindow.document.documentElement.scrollHeight + 'px'; } let latticeFile = document.location.href.split('?')[0].split('/').slice(0,-1).join('/')+'/'+machine+'_lattice.json'; if (machine.indexOf('simulator_')>-1) {latticeFile = document.location.href.split('?')[0].split('/').slice(0,-1).join('/')+'/'+'simulator.php?lattice&machine='+machine.split('simulator_')[1];} const menuParams = {machine: machineCaseSensitive.toLowerCase(), search: '', backgroundColor: '#333333'}; gui.title('PAnTHer - controls'); // if (navigator.userAgent.indexOf('Firefox/63')>-1) {$( "body" ).append('<button id="starter" style="align: center;height: 500px; width: 95%;background-color: #449944; font-size: 100px;" onClick="mystart()">START</button>');} function mystart() { panZoomPanther = svgPanZoom('#panther', {beforeZoom: myZoom, fit: false, contain: false}); $("#sname").on("keydown", searchText); $('#starter').hide(); } // Polyfill replaceAll() if (typeof String.prototype.replaceAll !== 'function') { String.prototype.replaceAll = function(search, replacement) { var target = this; return target.split(search).join(replacement); }; } // Polyfill for parentNode.replaceChildren() if (typeof Element.prototype.replaceChildren !== 'function') { Object.defineProperty(Element.prototype, 'replaceChildren', { configurable: true, writable: true, value: function replaceChildren(...nodes) { // Remove all existing child nodes while (this.firstChild) { this.removeChild(this.firstChild); } // Append new DOM objects this.append(...nodes); } }); } function toggleMachine(machine) { document.location = document.location.href.split('?')[0] + '?machine='+machine; /*if (document.location.search.indexOf('machine=')==-1) document.location = document.location.href + (document.location.href.indexOf('?')==-1? '?': '&') + 'machine='+machine; let search = document.location.search.split('machine=')[1].split('&')[0]; document.location = document.location.href.replace('machine='+search, 'machine='+machine);*/ } function compLink(id) { console.log('compLink()', id, document.getElementById("compdb").href); // window.open(document.getElementById("compdb").href, '_blank').focus(); window.open(id, 'components').focus(); // event.stopPropagation(); return false; } function searchText(e) { console.log('searchText()', e, e.keyCode || e.which, $("#sname").val().toUpperCase().replace('.','_')); if (e.keyCode == 13) findComponent($("#sname").val().toUpperCase()); // return -1; } function shrinkName(name) { return name.replaceAll('FEL0', 'FEL').replaceAll('PS_', 'PS').replaceAll('SIP_', 'SIP').replaceAll('KG0', 'KG').replaceAll('BC0', 'BC').replaceAll('_L0', 'L').replaceAll('_F0', 'F').replaceAll('_0', '').replaceAll('_', ''); } // if (navigator.userAgent.indexOf('Firefox/63')==-1) {gui.add(menuParams, 'machine', conf.machineList).onChange(function() {toggleMachine(menuParams.machine);});} gui.add(menuParams, 'machine', conf.machineList).onChange(function() {toggleMachine(menuParams.machine);}); gui.add(menuParams, 'search'); $('.controller.string:last').parent().append('<iframe id="talk" style="height: 30px;" src="./speech/talk.php?background=black&token='+speechToken+'" frameborder="0" scrolling="no" onload="resizeIframe(this)"></iframe>'); gui.addColor(menuParams, 'backgroundColor').onChange(function() {toggleParam('backgroundColor');}); menuParams.vlv = document.location.search.indexOf('vlv')>-1; gui.add(menuParams, 'vlv').name('vlv & bst').onChange(function() {toggleParam('vlv');}); menuParams.ps = document.location.search.indexOf('ps')>-1; gui.add(menuParams, 'ps').onChange(function() {toggleParam('ps');}); menuParams.measurement = document.location.search.indexOf('measurement')>-1; gui.add(menuParams, 'measurement').onChange(function() {toggleParam('measurement');}); const sstring = $('.controller.string').children().eq(1).children().eq(0); sstring.attr('id', 'sname'); sstring.attr('name', 'sname'); sstring.addClass("form-control sname"); function findComponent(name) { // https://stackoverflow.com/questions/5497318/replace-last-occurrence-of-character-in-string if (name.indexOf('.')==-1 && name.indexOf('_')>-1) name = name.replace(/_([^_]*)$/, '.' + '$1'); if (document.location.search.indexOf('debugfind')>-1) {console.log('findComponent('+name+')'); return 'OK';} let servicearea = false; console.log('lattice', lattice); if (lattice.servicearea) for (let i in lattice.servicearea.sections) { for (let j in lattice.servicearea.sections[i].components) { if (name.replace('.', '_')==lattice.servicearea.sections[i].components[j].name.replace('.', '_')) {servicearea = true;} for (let k in lattice.servicearea.sections[i].components[j].embedded) { if (name==lattice.servicearea.sections[i].components[j].embedded[k]) {servicearea = true;} } } } console.trace(name, servicearea, document.location.search.indexOf('servicearea'), document.location.search.indexOf('search='+name)); // return; if ((servicearea != document.location.search.indexOf('servicearea')>-1) && document.location.search.indexOf('search='+name)==-1) { document.location = './panther2d.php?machine='+machine+'&search='+name+(servicearea? '&servicearea': ''); } if (name==null) name = document.getElementById('sname').value; for (let i=0; i<alias.length; i++) if (alias[i][1]==name) name = alias[i][0]; let id = name.replace('.', '_'); document.getElementById('sname').value = name; console.log(name, typeof $('#'+name)[0], shrinkName(name), shrinkedNames.indexOf(shrinkName(name))); if (typeof $('#'+id)[0] == 'undefined') { const sindex = shrinkedNames.indexOf(shrinkName(id)); if (sindex>-1) id = names[sindex]; else return name+' Not found'; } console.log(name, window.innerWidth/2, $('#'+id)[0].getCTM().e, $('#'+id)[0].getCTM().f); // panZoomPanther.zoomAtPoint(10, {x: window.innerWidth/2 - $('#'+name)[0].getCTM().e, y: window.innerHeight/2 - $('#'+name)[0].getCTM().f}) panZoomPanther.zoom(10); // leave a delay between zoom and pan setTimeout(mypan, 1200, id); return 'OK'; } function mypan(name) { $('.label').show(); const x = window.innerWidth/2 - $('#'+name)[0].getCTM().e + panZoomPanther.getPan().x; const y = window.innerHeight/2 - $('#'+name)[0].getCTM().f + panZoomPanther.getPan().y; const m = $("svg")[0].getTransformToElement($('#'+name)[0]); panZoomPanther.pan({x: x, y: y}); setTimeout(pinhide, 100, name, $('#'+name).eq(0).attr('transform')); } function pinhide(name, transform) { // console.log(foundAnimate, foundScale); foundAnimate *= foundScale; if (foundAnimate>1.5) foundScale = 0.95; if (foundAnimate<1) {foundScale = 1.05; foundAnimate = 1;} else setTimeout(pinhide, 100, name, transform); $('#'+name).eq(0).attr('transform',transform+',scale('+foundAnimate+')'); } // $(function() {$(".sname").autocomplete({source: names, close: function(event, ui) {findComponent($('#sname').val()); }, select: function(event, ui) {findComponent(ui.item.value); return false;}});}); $(function() {$(".sname").autocomplete({source: names, select: function(event, ui) {findComponent(ui.item.value); return false;}});}); function toggleParam(name) { if (name=='measurement') { measurement = !measurement; measurementType = measurement; if (measurement) measurementEnable(); else measurementDisable(); return;} if (name=='backgroundColor') {$('body').css('backgroundColor', menuParams.backgroundColor); return;} const urlparam = {}; let search = document.location.search.replace('?', '').split('&'); if (search[0]=='') search.splice(0,1); const i=search.indexOf(name); if (i==-1) search.push(name); else search.splice(i,1); const res = search.join('&'); document.location = document.location.href.split('?')[0]+(res.length? '?'+res: ''); } // let ujiveStarted = false; async function subscribeSpeech() { mylog('subscribeSpeech()', 1, 2); let response = await fetch("./speech/talk.php?read&token="+speechToken); if (response.status == 502) { await subscribeSpeech(); } else if (response.status != 200) { // An error - let's show it mylog('subscribeSpeech() ERROR ', response.statusText); // Reconnect in one second await new Promise(resolve => setTimeout(resolve, 1000)); await subscribeSpeech(); } else { // Get and show the message let message = await response.text(); mylog('subscribeSpeech()', message); if (message=='ujive') { window.open('../ujive.php?token='+speechToken, 'ujive').focus(); /*if (!ujiveStarted) ujiveWindow = window.open('../ujive.php?token='+speechToken, '_blank').focus(); setTimeout(ujiveReset, 15000); ujiveStarted = true;*/ } else { const msg = findComponent(message); // return feedback fetch("./speech/talk.php?readresult="+msg+"&token="+speechToken); } // Call subscribeSpeech() again to get the next message await subscribeSpeech(); } } // function ujiveReset() {ujiveStarted = false;} function mylog(...args) { if (document.location.search.indexOf('debug')>-1) $('#debug').val($('#debug').val()+JSON.stringify(args)+'\n'); console.trace.apply(null); console.log(args, JSON.stringify(args), $('#debug').val()); } function initIndex(lattice) { const index = []; for (let l in lattice.conf.index) { if (l=='start') continue; const cmd = "findComponent('"+lattice.conf.index[l].replace('.','_')+"')"; index.push('<button onclick="'+cmd+'">'+l+'</button>'); } $('body').append('<div style="position: absolute; left: 5px; bottom: 5px;">'+index.join(' ')+'</div>'); } function toggleFacility(value) { console.log('toggleFacility', value, this); if (value) $('.'+this.property).show(); else $('.'+this.property).hide(); } function init() { fetch(latticeFile).then((response) => {return response.json();}).then((flattice) => { lattice = flattice; if (Object.keys(lattice).length>0) { gui.add(menuParams, 'measurement button', {Left: 'left', Right: 'right'}).onChange(measurementFacility); const measurementDevice = {'': ''}; const machineFolder = gui.addFolder('toggle facility'); for (let i in lattice) {if (i!='conf') facilities.push(i);} menuParams[' '] = false; machineFolder.add(menuParams, ' ').onChange(toggleFacility); // for (let i=0; i<facilities.length; i++) if (facilities[i]!='') {menuParams[facilities[i]] = true;} for (let i in lattice) { if (i == 'conf') continue; menuParams[i] = document.location.search.indexOf(i+'=hide')==-1; if (i != 'servicearea' && i != 'bl') measurementDevice[i] = i; console.log(i, menuParams[i]); // logic XOR https://stackoverflow.com/questions/2335979/is-there-anyway-to-implement-xor-in-javascript if ((document.location.search.indexOf('servicearea')==-1) != (i=='servicearea')) { initLattice(lattice[i].sections, i); // menuParams[i] = true; } else { initLattice(lattice[i].sections, i); console.log('.', i, menuParams[i]); if (document.location.search.indexOf('+servicearea')>-1) { // menuParams[i] = true; } else { menuParams[i] = false; } } if (!menuParams[i]) $('.'+i).hide(); } for (let i=0; i<facilities.length; i++) if (facilities[i]!='') {machineFolder.add(menuParams, facilities[i]).onChange(toggleFacility);} machineFolder.close(); gui.add(menuParams, 'measurement device', measurementDevice).onChange(measurementFacility); if (document.location.search.indexOf('measurement')==-1) measurementToggle(false); if (document.location.search.indexOf('servicearea')>-1) initSearch(lattice.servicearea.sections, 'servicearea'); if (typeof blm != 'undefined') blmMenu(lattice, facilities, menuParams); bpmInit(facilities); if (typeof bpmData != 'undefined') bpmMenu(lattice, facilities, menuParams); if (lattice.conf && lattice.conf.index) initIndex(lattice); } $('.scale').attr('transform', "scale(2)"); // {$('<div><iframe style="width: 100%;height:250px;" src="../misc/gauge.html?dark&r1only=1&r=100&max=360&throttlingPeriod=50&apply=rotate&val='+0+'"></iframe></div>').insertBefore('.function');} // panZoomPanther = svgPanZoom('#panther', {beforeZoom: myZoom, fit: false, contain: false}); // https://github.com/bastienmoulia/svg-pan-zoom-rotate // if( /Android|webOS|iPhone|iPad|Mac|Macintosh|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { panZoomPanther = svgPanZoom('#panther', { zoomEnabled: true, controlIconsEnabled: false, fit: 0, center: 1, maxZoom: maxZoom*2, customEventsHandler: eventsHandler, //beforeZoom: myZoom, }); } else { panZoomPanther = svgPanZoom('#panther', { zoomEnabled: true, zoomScaleSensitivity: 0.3, controlIconsEnabled: false, fit: false, center: false, minZoom: 0.2, maxZoom: maxZoom, beforePan: myPan, beforeZoom: myZoom, }); } if (document.location.search.indexOf('debug')>-1) { $('#tooltip').show(); $('#tooltip').css('width', '100%'); $('#tooltip').html('<textarea id="debug" style="height: 100px; width:100%;"></textarea>'); } subscribeSpeech(); let pan = [window.innerWidth/2, window.innerHeight/2]; if (document.location.search.indexOf('pan=')>-1) pan = document.location.search.split('pan=')[1].split('&')[0].split(','); if (document.location.search.indexOf('search=')>-1) findComponent(document.location.search.split('search=')[1].split('&')[0]); else { const default_zoom = typeof conf.default_zoom[machine.split('_')[0]]=='undefined'? conf.default_zoom.default: conf.default_zoom[machine.split('_')[0]]; const zoom = document.location.search.indexOf('zoom=')>-1? document.location.search.split('zoom=')[1].split('&')[0]-0: default_zoom; panZoomPanther.zoom(zoom); setTimeout(panZoomPanther.pan, 600, {x: pan[0], y: pan[1]}); } if (state) { fetch(conf.stateSrcUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const statSrc = data.toUpperCase().split('.').join('_').split(','); for (let j=0; j<statSrc.length; j++) { // if (statSrc[j].split('/')[2]=='POWER_SUPPLY' || statSrc[j].split('/')[2]=='INJECTION' || statSrc[j].split('/')[2]=='EXTRACTION') statSrc[j] = statSrc[j].split('/')[3].replace('PS', ''); } for (let i=0; i<status.length; i++) { if (status[i].name.indexOf('KICK')>-1) console.log('KICK state', i, status[i].name, statSrc); for (let j=0; j<statSrc.length; j++) { if (status[i].name.indexOf(statSrc[j])>-1) {status[i].statsrc = state; status[i].statindex = j;} } } // console.log('statSrc', data, statSrc, status); }); setInterval(updateStatus, 1000); } if (vlv) { for (let i in conf.bstmap) { if (i.indexOf('.')>-1) conf.bstmap[i.replace('.','_')] = conf.bstmap[i];} fetch(conf.vlvSrcUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const vlvSrc = data.toUpperCase().substring(14).split(','); for (let i=0; i<vlvs.length; i++) { for (let j=0; j<vlvSrc.length; j++) { const vlva = vlvSrc[j].split('/'); if (vlva.length < 4) continue; const name = vlva[3].replace('.','_'); if (vlvs[i].name.replace('.','_').indexOf(name)>-1) {vlvs[i].vlvsrc = state; vlvs[i].vlvindex = j; vlvs[i].type = 'vlv';} if (vlvSrc[j].split('/')[2].indexOf(conf.bstmap.base)>-1) { if (conf.bstmap[vlvs[i].name] && conf.bstmap[vlvs[i].name].indexOf(vlvSrc[j].split('/')[4])>-1) { vlvs[i].vlvsrc = 'bst'; vlvs[i].vlvindex = j; vlvs[i].type = 'bst'; } } } } //console.log('vlvSrc', data, vlvSrc, vlvs); }); setInterval(updateVlv, 1000); } $("#sname").on("keydown", searchText); if (lattice.conf && lattice.conf.modules) { for (let i=0; i<lattice.conf.modules.length; i++) { window[lattice.conf.modules[i]+'2d'](lattice, menuParams, compData); } } menuParams.gotoAdmin = function() {document.location = './admin.php';}; gui.add(menuParams, 'gotoAdmin').name('Admin'); menuParams.goto3D = function() {document.location = './panther.php?machine='+menuParams.machine;}; gui.add(menuParams, 'goto3D').name('3D'); }); } function showStatus(i, stat) { if (stat == 0 || stat == 'null' || stat == '' || stat == 'ON' || stat == 'RUNNING' || (!fel1 && $('#'+status[i].name)[0].classList[0]=='fel1') || (!fel2 && $('#'+status[i].name)[0].classList[0]=='fel2')) {$('#'+status[i].name).hide();} else if (stat == 'ON' || stat == 'RUNNING') {$('#'+status[i].name).show();document.getElementById(status[i].name).style.fill = conf.stateLabelColor[stat];$('#'+status[i].name).addClass('noblink');} else {$('#'+status[i].name).show(); document.getElementById(status[i].name).style.fill = conf.stateLabelColor[stat];$('#'+status[i].name).removeClass('noblink');} // console.log(i, status[i], stat); } function clearStatus() { $('.ps:not(.noblink)').hide(); } function updateStatus() { fetch(conf.stateUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const statVal = data.split(';'); // console.log(conf.stateUrl, statVal, status); for (let i=0; i<status.length; i++) { if (status[i].statsrc==state) { if (status[i].statindex) showStatus(i, statVal[status[i].statindex]); // console.log(statVal, status, status[i].name, i, status[i].statindex, statVal[status[i].statindex]); } } setTimeout(clearStatus, 600); }); } function updateVlv() { fetch(conf.vlvUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const vlvVal = data.split(':')[1].split(';'); // console.log('updateVlv()', conf.vlvUrl, vlvVal, vlvs); for (let i=0; i<vlvs.length; i++) { if (vlvs[i].type=='vlv') $('#'+vlvs[i].name).css('fill', vlvVal[vlvs[i].vlvindex]=='CLOSED'? 'red': (vlvVal[vlvs[i].vlvindex]=='OPENED'? 'limegreen': 'grey')); if (vlvs[i].type=='bst') { if (typeof vlvVal[vlvs[i].vlvindex] != 'string') continue; const val = vlvVal[vlvs[i].vlvindex].split(','); $('#'+vlvs[i].name).css('fill', val[0]=='true'? 'limegreen': (val[1]=='true'? 'red': 'grey')); } } }); } function rescale(x) {return x;} String.prototype.replaceAt = function(index, replacement) { return this.substring(0, index) + replacement + this.substring(index + replacement.length); }; function evalId(base) { if (base.indexOf('.')>-1) return base; return base.replaceAt(base.lastIndexOf('_'), '.').replace('RTBPM','BPM'); } function openTooltip(event) { if (document.location.search.indexOf('debug')==-1) { const type = this.href? this.href.baseVal.replace('#',''): (this.id.indexOf('BLM')>-1 || this.id.indexOf('BERGOZ')>-1? 'blm': '???'); console.log('openTooltip()',type, this.id, this, event, event.clientY); $('#tooltip').css('left', event.clientX+30); $('#tooltip').css('top', event.clientY+30); const embedded = (typeof this.dataset.embedded == 'undefined' || this.id[0]=='R')? '': ','+this.dataset.embedded; document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s='+type.replace('fast', '')+'¶m='+this.id + embedded; console.log('openTooltip(event)', type.toLowerCase(), type.toLowerCase().indexOf('beamline')); document.getElementById('tooltip').style.display = 'block'; if (hideTimeout!==false) clearTimeout(hideTimeout); hideTimeout = setTimeout(hideTooltip, 120000); const id = evalId(this.id); let buttons = '<button onClick="compLink(\''+conf.compdb + id+'\')">'+id+'</button> '; let rack = ''; if (typeof this.dataset.embedded != 'undefined') { const emb = this.dataset.embedded.split(','); for (let e=0; e<emb.length; e++) { buttons = buttons + '<button onClick="compLink(\''+evalId(emb[e])+'\')">'+evalId(emb[e])+'</button> '; } } else { for (let i=0; i<lattice.servicearea.sections.length; i++) { if (lattice.servicearea.sections[i].components) for (let j=0; j<lattice.servicearea.sections[i].components.length; j++) { if (lattice.servicearea.sections[i].components[j].embedded && lattice.servicearea.sections[i].components[j].embedded.indexOf('PS'+id)>-1) rack = 'rack <button onClick="compLink(\'https://puma-01.elettra.eu/spa/index.html?s='+lattice.servicearea.sections[i].components[j].name.split('_')[0].toLowerCase()+'¶m='+lattice.servicearea.sections[i].components[j].name+'\')">'+lattice.servicearea.sections[i].components[j].name+'</button> '; } } } buttons = buttons + rack; document.getElementById('compdb').innerHTML = buttons; document.getElementById('compdb').style.display = (type.indexOf('rv')==0 || type.indexOf('rc')==0 || type.indexOf('rid')==0 || type.indexOf('rd')==0 || type.indexOf('rps')==0 || type.indexOf('plc')==0)? 'none': 'block'; if (document.getElementById('compname')) document.getElementById('compname').innerHTML = id; // document.getElementById('compdb').href = conf.compdb + id; // document.getElementById("compdb").addEventListener("click", compLink); if (type.toLowerCase().indexOf('beamline')>-1) { document.getElementById('compdb').href = 'http://adam.elettra.trieste.it/projects/blcs/beamwatch/'; document.getElementById('compdb').innerHTML = 'search '+this.id+' in ADAM Beamwatch <span id="compname"/>'; } else if (machine=='elettra') document.getElementById('compdb').setAttribute("disabled", true); event.stopPropagation(); } else { for (let l in lattice) { if (l=='confg') continue; let servicearea = false; for (let i in lattice[l].sections) { for (let j in lattice[l].sections[i].components) { if (this.id==lattice[l].sections[i].components[j].name.replace('.', '_')) {mylog('openTooltip()', lattice[l].sections[i].components[j], lattice[l].sections[i].components[j].position-document.location.search.split('offset=')[1].split('&')[0]);} } } } } } function hideTooltip() { if (hideTimeout!==false) clearTimeout(hideTimeout); hideTimeout = false; // mylog('hideTooltip'); if (document.location.search.indexOf('debug')==-1) { document.getElementById('tooltipFrame').src = ''; document.getElementById('tooltip').style.display = 'none'; } } function transformLabel(x, y, beta, labelReverse) { if (typeof labelReverse == 'object') return "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+labelReverse[0])+") translate("+labelReverse[1]+" "+labelReverse[2]+")"; return labelReverse==180? "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta-90)+") translate(1800 100)": "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+90)+") translate("+(labelReverse? -350: 350)+" "+(labelReverse? 100: 200)+")"; } function appendSvg(tagName, attrib, onClickCall=false, text=false, myclass=false, appendTo="svg") { const elem = document.createElementNS("http://www.w3.org/2000/svg", tagName); if (onClickCall) elem.addEventListener("click", onClickCall, false); const titelem = document.createElementNS("http://www.w3.org/2000/svg", 'title'); const embedded = typeof attrib['data-embedded']=='object'? '\n'+attrib['data-embedded'].join(', '): ''; const aka = typeof attrib['data-aka']=='object' && attrib['data-aka'].length>0? '\naka: '+attrib['data-aka'].join(', '): ''; const title = document.createTextNode(attrib.id + aka + embedded); titelem.appendChild(title); elem.appendChild(titelem); if (text) { // const textNode = document.createTextNode(text); elem.appendChild(textNode); elem.innerHTML = text; } const jelem = $(elem); if (myclass) {/*mylog(elem, jelem, myclass);*/ elem.classList.add(myclass);} for (let i in attrib) { jelem.attr(i, attrib[i]); } const hook = appendTo.indexOf('.')==0? appendTo.replace(' ','_'): appendTo.replace('.','_').replace(' ','_'); $(hook).append(jelem); } function appendSearch(component, facility) { // mylog('appendSearch()',component, facility); if (component) { const comp = component.type.replace('booster', ''); if (comp && comp!=' ' && $('#'+comp)[0]) { const id = extractId(component.name); names.push(id[0]); shrinkedNames.push(shrinkName(id[0])); if (facility=='servicearea') serviceareanames.push(id[0]); if (component.embedded) { for (let j=0; j<component.embedded.length; j++) { names.push(component.embedded[j]); shrinkedNames.push(shrinkName(component.embedded[j])); alias.push([id[0],component.embedded[j]]); } } if (component.alias) { for (let j=0; j<component.alias.length; j++) { names.push(component.alias[j]); shrinkedNames.push(shrinkName(component.alias[j])); alias.push([id[0],component.alias[j]]); } } } } } function appendLabel(id, labelclass, display, x, y, beta, labelReverse, suffix='label', fontSize=1, appendTo="svg") { if ((beta+3600)%360 <180 && typeof labelReverse != 'object') labelReverse = [-90, -250, -100]; appendSvg("text", { id: (id+suffix).replace(' ','_'), filter: "url(#solid)", class: labelclass, x:0, y:0, style:"display: "+display, fill:"white", stroke:"#101020","stroke-width":0, "font-family":"Arial", "font-size":100*fontSize, "font-weight":"700", "text-anchor": (labelReverse? "end": "start"), transform: transformLabel(x, y, beta, labelReverse) }, false, appendTo=="svg"? id: '', false, appendTo); // }, false, id, false, appendTo); } function appendLabel2(param, labelclass, display, x, y, beta, labelReverse, suffix='label') { const id = param.name; const fontsize = labelclass.indexOf('bl')>-1? 800: 500; // console.log("appendLabel2()",param, labelclass, display, x, y, beta, labelReverse); if (labelclass.indexOf('bl')>-1 && typeof labelReverse != 'object') { labelReverse = [180, 17000, param.type=='beamlineUp'? -500: +800]; if ((beta+3690)%360 < 180) labelReverse = [0, -14000, param.type=='beamlineUp'? 1000: -300]; } else if ((beta+3600)%360 < 180 && typeof labelReverse != 'object') labelReverse = [-90, -250, -100]; appendSvg("text", { id: id+suffix, // filter: "url(#solid)", class: labelclass, x:0, y:0, style:"display: block", fill:"red", stroke:"pink","stroke-width":5, "font-family":"Arial", "font-size":fontsize, "font-weight":"700", "text-anchor": (labelReverse? "end": "start"), transform: transformLabel(x, y, beta, labelReverse) }, false, id); } function appendComponent(components, x0, y0, x1, y1, facility) { const dx = x1 - x0; const dy = y1 - y0; const d = Math.sqrt(dx*dx + dy*dy); const beta = 180*Math.atan2(y0-y1, x0-x1)/Math.PI; if (components) for (let i=0; i<components.length; i++) { const comp = components[i].type; // .replace('booster', ''); let x = x0+components[i].position/d*dx; let y = y0+components[i].position/d*dy; if (components[i].offset2d) {x += components[i].offset2d[0]; y += components[i].offset2d[1];} if (typeof blm != 'undefined' && comp=='blm') { const name = components[i].name.replace('BPM','BLM'); // mylog('appendComponent(), ', name, blm.obj); blm.obj.push(name); blm.dir.push(beta); appendSvg("rect", {id:name, name:name, x:0, y:0, width:40, height:40, rx:20, ry:20, transform:"translate("+rescale(x)+" "+rescale(y)+") rotate("+(name.indexOf('_L')>-1? beta + 180: beta)+")"}, openTooltip, false, "blm"); } if (typeof bpmData != 'undefined' && comp=='bpm') { // mylog('appendComponent()',components[i], x0, y0, x1, y1, beta, facility, 180*Math.atan2(y0-y1, x0-x1)/Math.PI); bpmData[facility].obj.push(components[i].name); bpmData[facility].dir.push(beta); bpmData[facility].pos.push([rescale(x), rescale(y)]); } if (typeof bpmData != 'undefined' && comp=='corrector') { corr[facility].obj.push(components[i].name); corr[facility].dir.push(beta); corr[facility].pos.push([x, y]); } compData[facility].obj.push(components[i].name); compData[facility].dir.push(beta); compData[facility].pos.push([rescale(x), rescale(y)]); if ($('#'+comp)[0]) { // mylog('components['+i+']',components[i]); const id = extractId(components[i].name); const section = components[i].name.indexOf('_')>-1? components[i].name.split('_')[1].split('.')[0]+' ': ''; const offset = components[i].type == 'beamlineUp'? "-2100 -3500": "0 -200"; const transform = "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+180)+") translate("+offset+")"; const compid = comp + (components[i].length > 0 && $('#'+comp+'_'+components[i].length)[0]? '_'+components[i].length: ''); const aka = components[i].alias && components[i].alias.length? components[i].alias: []; const compClass = typeof components[i].class == 'undefined'? '': ' '+components[i].class; appendSvg("use", {href:"#"+compid, id: id[0], name:components[i].name, "data-embedded":components[i].embedded, "data-aka": aka, class: comp+' '+section+facility+compClass, style:"cursor: pointer", transform:transform}, openTooltip); if (components[i].type == 'label' || components[i].type == 'beamlineDown' || components[i].type == 'beamlineUp') appendLabel2(components[i], section+facility, 'none', x, y, beta, components[i].labelReverse); else appendLabel(components[i].name, comp+' label '+section+facility+compClass, 'none', x, y, beta, components[i].labelReverse); if (id[0].indexOf('<')==-1) { appendSvg("g", {"id": id[0]+'_g'}); appendLabel(components[i].name, comp+' value '+section+facility, 'none', x+600, y+400, 90, true, 'value', 0.7, '#'+id[0]+'_g'); } names.push(id[0]); shrinkedNames.push(shrinkName(id[0])); if (typeof components[i].embedded == 'object') { for (let j=0; j<components[i].embedded.length; j++) {names.push(components[i].embedded[j]); alias.push([id[0],components[i].embedded[j]]);} } if (typeof components[i].alias == 'object') { for (let j=0; j<components[i].alias.length; j++) {names.push(components[i].alias[j]); alias.push([id[0],components[i].alias[j]]);} } if (id[1]) {names.push(id[1]); shrinkedNames.push(shrinkName(id[1])); alias.push(id);} if (state) { if (components[i].ps) { for (let pi=0; pi<components[i].ps.length; pi++) { const name = components[i].ps[pi].replace('PS','').replace('.','_') + '_status'; status.push({name: name}); appendSvg('circle', {id: name, class: facility+' ps', style:"display: none", cx: 150+pi*100, cy: 200, transform:transform, r: 80, stroke: 10, strokeColor: 'blue', fill: 'gray'}, false, false, 'status'); } } else { status.push({name: id[0]+'_status'}); appendSvg('circle', {id: id[0]+'_status', class: facility+' ps', style:"display: none", cx: 200, cy: 200, transform:transform, r: 80, stroke: 10, strokeColor: 'blue', fill: 'gray'}, false, false, 'status'); } if (components[i].name.toUpperCase().indexOf('KICK')>-1) console.log('PS: ', components[i].name, components[i].name.toUpperCase().indexOf('KICK'), status); } if (vlv && (components[i].type == 'vlv' || components[i].type == 'bst')) { // appendSvg('rect', {id: id[0]+'_disable', style:"display: none", x: x+75, y: y-90, width: 50, height: 200, stroke: 10, fill: 'black'}, false, false, 'vlvs'); vlvs.push({name: id[0], type: components[i].type}); } } } } /*if (!String.prototype.replaceAll) { String.prototype.replaceAll = function(search, replace) { return this.split(search).join(replace); }; }*/ function extractId(name) { if (name.indexOf('(')>-1) { let tok = name.split('('); return [tok[0].replaceAll('.','_').replaceAll(' ','_').replace(/_+$/, ''), tok[1].replaceAll('.','_').replaceAll(' ','_').replaceAll(')','').replace(/_+$/, '')]; } // .replace(/_+$/, '') => https://stackoverflow.com/questions/8141718/how-to-trim-specific-characters-from-the-end-of-the-javascript-string return [name.replaceAll('.','_').replaceAll(' ','_').replace(/_+$/, ''), false]; } function highlightobjects(objclass) { $("use").css('opacity',0.3); $("text").css('opacity',0.3); $("."+objclass).css('opacity',1); } function magnifyobjects(objclass) { $("text").css('opacity',0.15); $("text."+objclass).css('opacity',1); $('.scale').attr('transform', "scale(1) translate(90,100)"); let x = -40; let y = -40; let s = 3; if (objclass=="vlv") {x = -50; y = -50; s = 5;} $('.'+objclass+'scale').attr('transform', "scale("+s+") translate("+x+","+y+")"); } function initSearch(sections, facility) { console.log('initSearch()', sections, facility); for (i=0; i<sections.length; i++) { // if(i>1) break; if (sections[i].bending && sections[i].bending.type) { // console.log('names.push()', sections[i].bending.name); names.push(sections[i].bending.name.replace('.','_')); shrinkedNames.push(shrinkName(sections[i].bending.name.replace('.','_'))); } if (typeof sections[i].components == 'object') for (let j=0; j<sections[i].components.length; j++) appendSearch(sections[i].components[j], facility); } } function initLattice(sections, facility) { initSearch(sections, facility); if (document.location.search.indexOf('facility=')>-1 && document.location.search.split('facility=')[1].split('&')[0].split(',').indexOf(facility)==-1) return; if (typeof bpmData != 'undefined') { compData[facility] = {obj: [], dir: [], pos: [], index: []}; bpmData[facility] = {obj: [], dir: [], pos: []}; corr[facility] = {obj: [], dir: [], pos: []}; } // vacuum chamber let d = ''; let m = true; let wall = false; let w = 20; for (let i=0; i<sections.length; i++) { d = d + (m? 'M ': ' L ')+rescale(sections[i].start.x)+' '+rescale(sections[i].start.z); if (i<sections.length-1 && typeof sections[i+1].chamber == 'undefined') {m = true;} else if (i<sections.length-1 && (sections[i+1].chamber.type=="chamber" || sections[i+1].chamber.type.indexOf("wall")>-1)) {m = false;} if (i<sections.length-1 && typeof sections[i+1].chamber != 'undefined' && sections[i+1].chamber.type.indexOf("wall")>-1) { if (sections[i+1].chamber.type.indexOf('_')>-1) w = sections[i+1].chamber.type.split('_')[1]-0; wall = true; } } if (sections[0].chamber && sections[0].chamber.type=='chamber') d = d + ' Z'; if (facility=='sr') console.log('sections', sections, 'd', d); if (d!='') { if (wall) { console.log('w', w, sections, d); //appendSvg("path", {d: d, fill: "none", stroke: "#990000", "stroke-dasharray": "200 200", "stroke-width": 50, id:"wall"+facility}); appendSvg("path", {d: d, fill: "none", stroke: "#f0fff0", "stroke-width": w+20, id:"chamber"+facility, class: facility}); appendSvg("path", {d: d, fill: "none", stroke: "#ff0000", "stroke-width": w, id:"chamber"+facility, class: facility}); } else appendSvg("path", {d: d, fill: "none", stroke: "#999999", "stroke-width": 20, id:"chamber"+facility, class: facility}); } // bending let j = 0; let i = sections.length - 1; let k = sections.length - 2; let alpha = 180/Math.PI*Math.atan2(sections[j].start.z-sections[i].start.z, sections[j].start.x-sections[i].start.x); for (i=0; i<sections.length; i++) { // if(i>1) break; j = (i + 1) % sections.length; k = (i + sections.length - 1) % sections.length; let beta = 180/Math.PI*Math.atan2(sections[j].start.z-sections[i].start.z, sections[j].start.x-sections[i].start.x); let gamma = (beta+alpha)/2; if (sections[k] && sections[j].start.z<sections[i].start.z && sections[i].start.z>sections[k].start.z) gamma = gamma + 180; if (alpha>beta && gamma<0) gamma = gamma + 180; if (sections[i].bending && sections[i].bending.type) { const section = sections[i].bending.name.indexOf('_')>-1? sections[i].bending.name.split('_')[1].split('.')[0]+' ': ''; const comp = sections[i].bending.type.replace('dipolefermi','bending'); const compid = comp + (sections[i].bending.length > 0 && $('#'+comp+'_'+sections[i].bending.length)[0]? '_'+sections[i].bending.length: ''); appendSvg("use", {href:"#"+compid, id:sections[i].bending.name.replace('.','_'), class: 'bending '+section+facility, style:"cursor: pointer", transform:"translate("+rescale(sections[i].start.x)+" "+rescale(sections[i].start.z)+") rotate("+gamma+") translate(-600 -200)"}, openTooltip); appendLabel(sections[i].bending.name, 'bending '+section+facility, 'block', sections[i].start.x, sections[i].start.z, gamma+180, sections[i].bending.labelReverse); } alpha = beta; appendComponent(sections[i].components, sections[i].start.x, sections[i].start.z, sections[j].start.x, sections[j].start.z, facility); } } $(document).ready(function() { $("svg").attr('height', window.innerHeight+'px'); init(); }); var eventsHandler; let myhammer; eventsHandler = { haltEventListeners: ['touchstart', 'touchend', 'touchmove', 'touchleave', 'touchcancel','tap', 'pinch','pinchstart','pinchmove'], init: function(options) { var instance = options.instance, initialScale = 1, pannedX = 0, pannedY = 0; // mylog('init', options, panZoomPanther); myhammer = Hammer(options.svgElement, { inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput }); myhammer.get('pinch').set({enable: true}); myhammer.on('panstart panmove', function(ev){ if (ev.type === 'panstart') { pannedX = 0; pannedY = 0; } panZoomPanther.panBy({x: ev.deltaX - pannedX, y: ev.deltaY - pannedY}); pannedX = ev.deltaX; pannedY = ev.deltaY; }); myhammer.on('pinchstart pinchmove', function(ev){ if (ev.type === 'pinchstart') { initialScale = panZoomPanther.getZoom(); panZoomPanther.zoomAtPoint(initialScale * ev.scale, {x: ev.center.x, y: ev.center.y}, zoom, ev.scale); // mylog(''); mylog('panZoomPanther.zoomAtPoint('+(initialScale * ev.scale)+', {x: '+ev.center.x+', y: '+ev.center.y+'})'); } if (ev.type === 'pinchmove') { oldZoom = zoom; zoom = initialScale * ev.scale; // mylog('zoom', zoom, ev.center.x); point.x = ev.center.x; visibleX = 0 - point.x/zoom; visibleWidth = document.getElementById('panther').clientWidth/zoom; } panZoomPanther.zoomAtPoint(initialScale * ev.scale, {x: ev.center.x, y: ev.center.y}); zoom = panZoomPanther.getZoom(); if (zoom>2) $('.label').show(); else $('.label').hide(); // mylog('panZoomPanther.zoomAtPoint('+(initialScale * ev.scale)+', {x: '+ev.center.x+', y: '+ev.center.y+'})', zoom, ev.scale); }); options.svgElement.addEventListener('touchmove', function(e){e.preventDefault(); }); }, destroy: function(){ myhammer.destroy(); } }; function myPanZoomDelayed(event) { const urlPan = (document.location.search.indexOf('pan=')==-1)? [0, 0]: document.location.search.split('pan=')[1].split('&')[0].split(','); const urlZoom = (document.location.search.indexOf('zoom=')==-1)? 1: document.location.search.split('zoom=')[1].split('&')[0]; if (Math.abs(panZoomPanther.getPan().x - urlPan[0]) + Math.abs(panZoomPanther.getPan().y - urlPan[1]) > 1) { setUrl('pan', Math.round(panZoomPanther.getPan().x*100)/100+','+Math.round(panZoomPanther.getPan().y*100)/100); } if (Math.abs(panZoomPanther.getZoom() - urlZoom) > 0.1) { setUrl('zoom', Math.round(panZoomPanther.getZoom()*100)/100); } if (event) event.preventDefault(); visibleX = 0 - point.x/zoom; visibleWidth = document.getElementById('panther').clientWidth/zoom; // mylog('myPanZoomDelayed, event', event, 'point', point, 'zoom:', zoom, 'visibleX', visibleX, 'visibleWidth', visibleWidth); myPanZoomTimer = null; oldZoom = zoom; } let urlCount = -1; function setUrl(name, value) { let t = + new Date(); if (t - historytime < 100) return; historytime = t; parameters[name] = value; const pp = []; for (let i in parameters) {pp.push(i+'='+parameters[i]);} const url = document.location.origin+document.location.pathname+'?'+pp.join('&').replaceAll('=undefined', '').replace('search=','s='); // console.log('setUrl()',name, value, url); if (urlCount==1) window.history.pushState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url); else if (urlCount>1) window.history.replaceState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url); urlCount++; } function myPan(oldPan, newPan) { setUrl('pan', Math.round(newPan.x*100)/100+','+Math.round(newPan.y*100)/100); point.x = newPan.x; point.y = newPan.y; if (myPanZoomTimer == null && document.location.search.indexOf('&autoPanZoom')==-1) myPanZoomTimer = setTimeout(myPanZoomDelayed, panZoomTime); // mylog('myPan', p,q); } function myZoom(oldScale, newScale) { clearmeasurement(); setUrl('zoom', Math.round(newScale*100)/100); zoom = newScale; if (newScale>3.9) { $('.bending').css('font-size', 150); $('.label').show(); } else { $('.bending').css('font-size', newScale<1? 800: 350); $('.label').hide(); } for (let i=1; i<=15; i++) if (newScale>i) $('.zoom'+i).show(); else $('.zoom'+i).hide(); for (let i=1; i<facilities.length; i++) if(!menuParams[facilities[i]]) $('.'+facilities[i]).hide(); if (myPanZoomTimer == null && document.location.search.indexOf('&autoPanZoom')==-1) myPanZoomTimer = setTimeout(myPanZoomDelayed, panZoomTime); // mylog('myZoom', zoom); } /* https://github.com/dagrejs/dagre-d3/issues/202 */ SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) { return elem.getScreenCTM().inverse().multiply(this.getScreenCTM()); };