// jshint esversion: 6 import * as THREE from 'three'; import Stats from 'three/addons/libs/stats.module.js'; import {GUI} from 'three/addons/libs/lil-gui.module.min.js'; import {OrbitControls} from 'three/addons/controls/OrbitControls.js'; import {MapControls} from 'three/addons/controls/MapControls.js'; import {EffectComposer} from 'three/addons/postprocessing/EffectComposer.js'; import {RenderPass} from 'three/addons/postprocessing/RenderPass.js'; import {OutlinePass} from 'three/addons/postprocessing/OutlinePass.js'; import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js'; /* import { WebGPURenderer } from 'three/examples/jsm/renderers/WebGPURenderer.js'; import WebGPU from 'three/addons/capabilities/WebGPU.js'; import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js'; */ import {ellipticaltubeGeometry} from 'components/ellipticaltube.js'; import {chamber} from 'components/chamber.js'; import {wall} from 'components/wall.js'; import * as componentCreator from 'bundle'; import {conf} from './panther_conf.js'; import * as blmres from './blm.js'; let firstUrl = true; let historytime = + new Date(); function setUrl(name, value) { let t = + new Date(); if (t - historytime < 200) 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('&').replace('=undefined', ''); if (firstUrl) window.history.pushState({"html":'panther.php',"pageTitle":'PAnTHer'},"", url); else window.history.replaceState({"html":'panther.php',"pageTitle":'PAnTHer'},"", url); firstUrl = false; } function compLink(event) { window.open(document.getElementById("compdb").href, '_blank').focus(); event.stopPropagation(); return false; } const pa = document.location.search.replace('?','').split('&'); const parameters = {}; for (let i=0; i<pa.length; i++) {const p = pa[i].split('='); parameters[p[0]] = p[1];} let cameraStarted = false; const machineCaseSensitive = document.location.search.indexOf('machine=')>-1? document.location.search.split('machine=')[1].split('&')[0]: conf.default_machine; const machine = machineCaseSensitive.toLowerCase(); 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];} window.names = []; window.alias = []; let lattice; let dipoleNum = 0; const raycaster = new THREE.Raycaster(); const pointer = new THREE.Vector2(); const component = []; const facilities = []; const status = []; let selectedObjects = []; let found = false; const vlv = document.location.search.indexOf('vlv')>-1; const vlvs = []; const ps = document.location.search.indexOf('ps')>-1; const demo = document.location.search.indexOf('demo')>-1; // || document.location.search.indexOf('machine=elettra2')>-1; if (demo) document.getElementById('demo').style.display = 'block'; let camera, controls, scene, renderer; let composer, outlinePass; let tooltipObject = null; let x1, y1, z1, x2, y2, z2, rx1, ry1, rz1, rx2, ry2, rz2, tang, alpha, d, den; const envelopeMaterial = new THREE.MeshBasicMaterial({color: 0x00f0f0, transparent: true, opacity: 0.5, side: THREE.DoubleSide,}); let envelopeCounter = 0, envelopeIndex=0; let envelopeSrc = ''; const envelopeMesh = []; const envelopeSize = []; const envelopeFactorX = 5000000; const envelopeFactorY = 10000000; const envelopeNum = 1286; const envelopeOffset = 83; const bpm = {radius: 100, srv: './bpm.php', mesh: {}}; const bpmMesh = {}; const bpmReferenceMesh = {}; const bpmPoints = {}; const bpmSkip = {}; const bpmIndex = {}; const bpmData = {}; const compData = {}; const compBuffer = {}; const loader = new GLTFLoader(); let fast = document.location.search.indexOf('fast')>-1? document.location.search.split('fast')[1].split('&')[0].split(','): false; let premium = document.location.search.indexOf('premium')>-1? document.location.search.split('premium')[1].split('&')[0].split(','): false; const real = (document.location.search.indexOf('real')>-1 || (machine=='elettra2' && premium)) && document.location.search.indexOf('stl')==-1 && document.location.search.indexOf('high')==-1; const stl = document.location.search.indexOf('stl')>-1; const high = document.location.search.indexOf('high')>-1; const blm = {oldIndex: null, reader: false, acqTime: null}; let latticenodes = 0; const Ydefault = 100; let params = {machine: machineCaseSensitive, search: '', envelopeNum: 0, backgroundColor: '#777777'}; let fel1 = false; let fel2 = false; 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; }) .catch(error => {console.log("Fetch error", error);}); } const gui = new GUI(); gui.add(params, 'machine', conf.machineList).onChange(function() {toggleMachine(params.machine);}); params.mode = document.location.search.indexOf('fast')>-1? 'fast': document.location.search.indexOf('premium')>-1? 'premium': document.location.search.indexOf('plus')>-1? 'plus': 'normal'; gui.add(params, 'mode', conf.modes).onChange(function() {toggleParam('mode', params.mode);}); params.vlv = document.location.search.indexOf('vlv')>-1; gui.add(params, 'vlv').name('vlv & bst').onChange(function() {toggleParam('vlv');}); params.ps = document.location.search.indexOf('ps')>-1; gui.add(params, 'ps').onChange(function() {toggleParam('ps');}); gui.title('PAnTHer - controls'); gui.add(params, 'search'); // .onKeyup(function() {console.log('search');}); const sstring = $('.controller.string').children().eq(1).children().eq(0); sstring.attr('id', 'sname'); sstring.attr('name', 'sname'); sstring.addClass("form-control sname"); gui.addColor(params, 'backgroundColor').onChange(function() {toggleParam('backgroundColor');}); params.far = typeof conf.default_far[machine]!='undefined'? conf.default_far[machine]: conf.default_far.default; gui.add(params, 'far', 50, 500).onChange(function() {setFar(params.far);}); params.tour = document.location.search.indexOf('tour=')>-1; gui.add(params, 'tour').onChange(function() {switchTour();}); const debugcamera = document.location.search.indexOf('debugcamera')>-1; if (debugcamera) $('body').append('<div class="debug" style="top: 0; position: fixed;"> x: <input size="5" id="x"/>, y: <input size="5" id="y"/>, z: <input size="5" id="z"/>, cx: <input size="5" id="cx"/>, cy: <input size="5" id="cy"/>, cz: <input size="5" id="cz"/>'); const highlight = document.location.search.indexOf('highlight=')==-1? []: document.location.search.split('highlight=')[1].split('&')[0].split(','); params.highlightScale = document.location.search.indexOf('highlightScale=')==-1? (highlight.length? 0.5: 1): document.location.search.split('highlightScale=')[1].split('&')[0]-0; params.highlightShrink = document.location.search.indexOf('highlightShrink=')==-1? 0.2: document.location.search.split('highlightShrink=')[1].split('&')[0]-0; const stats = new Stats(); // window.stats = stats; if (document.location.search.indexOf('stats')>-1) { stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom document.body.appendChild(stats.dom); } export function bpm_reference(action) { if ($('.name:contains("bpmReference")') && $('.name:contains("bpmReference")').parent()) { const name = $('.name:contains("bpmReference")').parent().select('.widget').children().eq(1).children('input').val(); if (action=='save') fetch(bpm.srv+'?save='+name+'&content='+JSON.stringify(bpmData)).then((response) => {return response.text();}).then((res) =>{alert(res=='OK'? 'BPM reference saved': res);}).catch(error => {console.log("Fetch error", error);}); else fetch(bpm.srv+'?open='+name).then((response) => {return response.json();}).then((res) =>{ for (let facility in res) { // if (facility=='bts') use https://puma-01.elettra.eu/rchan.php?json&valueOnly&src=srv-tango-sre-01.ecs.elettra.trieste.it:20000/fb/bts/traj/SensorReferenceValues bpmIndex[facility] = lattice[facility].bpm.offset; const path = bpmPath(res[facility], facility); const skiplen = lattice[facility].bpm.skip? lattice[facility].bpm.skip.length: 0; const bpmGeometry = new THREE.TubeGeometry(path, lattice[facility].bpm.length-1-skiplen, bpm.radius, 16, (typeof lattice[facility].sections[0].chamber)!='undefined'); const material = new THREE.MeshBasicMaterial({color: 0x00ff80, transparent: true, opacity: 0.5,}); bpmReferenceMesh[facility] = new THREE.Mesh(bpmGeometry, material); facilities[facility].add(bpmReferenceMesh[facility]); } }) .catch(error => {console.log("Fetch error", error);}); } } window.bpmreference = bpm_reference; function updateBpm(data, facility) { console.log('updateBpm(), data:', data, ', facility:', facility, ', bpmMesh', bpmMesh); bpmData[facility] = data; bpmMesh[facility].geometry.dispose(); bpmIndex[facility] = lattice[facility].bpm.offset; const path = bpmPath(data, facility); console.log('updateBpm(), data:', data, ', facility:', facility, ', bpmMesh', bpmMesh, 'path', path); const len = lattice[facility].bpm.length-1-(typeof lattice[facility].bpm.skip=='undefined'? 0: lattice[facility].bpm.skip.length); const bpmGeometry = new THREE.TubeGeometry(path, len, bpm.radius, 16, (typeof lattice[facility].sections[0].chamber)!='undefined'); bpmMesh[facility].geometry = bpmGeometry.clone(); bpmMesh[facility].geometry.computeVertexNormals(); bpmMesh[facility].geometry.normalsNeedUpdate = true; bpmMesh[facility].geometry.verticesNeedUpdate = true; bpmMesh[facility].geometry.dynamic = true; data = null; } function readBpms() { fetch(conf.bpmUrl) .then((response) => {return response.json();}) .then((data) => { for (let facility in data) { updateBpm(data[facility], facility); } bpmTimes[bpmTimeIndex] = + new Date(); bpmTimeIndex = (bpmTimeIndex + 1) % bpmTimeLen; if (bpmWait > 0) setTimeout(readBpms, bpmWait); if (bpmTimes[bpmTimeIndex] > 0) { const bpmfreq = 1000 / (+ new Date() - bpmTimes[bpmTimeIndex]) * bpmTimeLen; $('#bpmfreq').val(bpmfreq.toPrecision(2) + ' fps'); // console.log(bpmfreq); } }) .catch(error => {console.log("Fetch error", error, conf.bpmUrl);}); } function bpmPath(data, facility) { let len = lattice[facility].bpm.length - bpmSkip[facility].length; if (len>bpmPoints[facility].length) len = bpmPoints[facility].length; // if (data!=-1) console.log('bpmDbg', len, facility, data, bpmPoints[facility].length); const bpmCurve = []; const bpmFactor = conf.bpmFactor[facility]; for (let i=0; i<len; i++) { const j = (i) % len; const tx = bpmPoints[facility][i][0].x + Math.cos(bpmPoints[facility][i][1])*(data.Hor? data.Hor[j]*1000*bpmFactor*params.bpmZoomH: 0); const ty = Ydefault + bpmPoints[facility][i][0].y + (data.Ver? data.Ver[j]*1000*bpmFactor*params.bpmZoomV: 0); const tz = bpmPoints[facility][i][0].z - Math.sin(bpmPoints[facility][i][1])*(data.Hor? data.Hor[j]*1000*bpmFactor*params.bpmZoomH: 0); if (!isNaN(tx) && !isNaN(ty) && !isNaN(tz)) bpmCurve.push(new THREE.Vector3(tx, ty, tz)); } // console.log(facility, bpmCurve); return new THREE.CatmullRomCurve3(bpmCurve); } let bpmInited = false; const bpmTimeLen = 3; let bpmTimeIndex = 0; const bpmTimes = new Array(bpmTimeLen).fill(0); const bpmWait = document.location.search.indexOf('bpmWait=')>-1? document.location.search.split('bpmWait=')[1].split('&')[0]-0: 0; function bpmAdd(facility) { console.log('bpmDbg(), facility:', facility); if (lattice[facility] && lattice[facility].bpm && (document.location.search.indexOf('&bpm')>-1 || document.location.search.indexOf('?bpm')>-1)) { bpmIndex[facility] = lattice[facility].bpm.offset; const path = bpmPath(-1, facility); const skiplen = lattice[facility].bpm.skip? lattice[facility].bpm.skip.length: 0; const bpmGeometry = new THREE.TubeGeometry(path, lattice[facility].bpm.length-1-skiplen, bpm.radius, 16, (typeof lattice[facility].sections[0].chamber)!='undefined'); const material = new THREE.MeshBasicMaterial({color: 0x8000ff, transparent: true, opacity: 0.5,}); bpmMesh[facility] = new THREE.Mesh(bpmGeometry, material); facilities[facility].add(bpmMesh[facility]); if (!bpmInited) { bpmInited = true; readBpms(); if (document.location.search.indexOf('bpm=')==-1 && bpmWait==0) setInterval(readBpms, 300); bpmTimes[bpmTimeIndex] = + new Date(); bpmTimeIndex = (bpmTimeIndex + 1) % bpmTimeLen; if (bpmWait > 0) setTimeout(readBpms, bpmWait); } } console.log('bpmDbg(), bpmMesh:', bpmMesh, bpmMesh[facility]); } init(); export function setShrink(v) {console.log(params.highlightShrink, v); params.highlightShrink = v; toggleParam('highlightShrink', v); } window.shrink = setShrink; function mytoggle() {console.log('mytoggle()', this); toggleParam(this.property+(this.property.indexOf('highlight')==-1? '=hide': ''), this.value);} // https://lil-gui.georgealways.com/#Controller#onChange function initIndex(lattice) { const index = []; for (let l in lattice.conf.index) { let servicearea = false; for (let i in lattice.servicearea.sections) { for (let j in lattice.servicearea.sections[i].components) { if (lattice.conf.index[l]==lattice.servicearea.sections[i].components[j].name) {servicearea = true;} } } const cmd = 'window.findComponent('+"'"+lattice.conf.index[l]+"'"+', '+(servicearea? 'true': 'false')+')'; index.push('<button onclick="'+cmd+'">'+l+'</button>'); } $('body').append('<div style="position: absolute; left: 5px; bottom: 5px;">'+index.join(' ')+'</div>'); } function initShortcut(lattice) { const index = []; for (let l in lattice.conf.shortcut['3d']) { const cmd = "document.location = document.location.pathname + '?"+lattice.conf.shortcut['3d'][l]+"';"; index.push('<button onclick="'+cmd+'" id="'+l.toLowerCase().split(' ').join('')+'">'+l+'</button>'); } $('body').append('<div style="position: absolute; left: 5px; bottom: 5px;">'+index.join(' ')+'</div>'); if(document.location.search.indexOf('ps')>-1) $('#powersupplies').css('background-color', 'limegreen'); } fetch(latticeFile).then((response) => {return response.json();}).then((flattice) => { lattice = flattice; const blmfacilities = ['']; const machineFolder = gui.addFolder('toggle facility'); if (Object.keys(lattice).length>1) { const serviceareadefault = document.location.search.indexOf('servicearea')>-1 && document.location.search.indexOf('servicearea=')==-1; for (let i in lattice) { if (i=='conf') { params[i] = false; continue; } params[i] = document.location.search.indexOf(i+'=hide')==-1 && !(serviceareadefault && document.location.search.indexOf(i)==-1); if (i=='servicearea' && document.location.search.indexOf(i)==-1) params[i] = false; machineFolder.add(params, i).onChange(mytoggleFacility); if (lattice[i].blm) {blmfacilities.push(i);} // console.log('fetch()', latticeFile, i, params[i]); if (lattice[i].bpm && (typeof params.bpm == 'undefined')) { params.bpm = document.location.search.indexOf('&bpm')>-1 || document.location.search.indexOf('?bpm')>-1; gui.add(params, 'bpm').onChange(function() {toggleParam('bpm');}); if (params.bpm) { $('.controller.boolean:last').append('<input id="bpmfreq" type="text" spellcheck="false" aria-labelledby="lil-gui-name-6">'); params.bpmZoomH = 1; gui.add(params, 'bpmZoomH', 0.01, 100); params.bpmZoomV = 1; gui.add(params, 'bpmZoomV', 0.01, 100); params.bpmReference = '2GeV'; gui.add(params, 'bpmReference'); if ($('.name:contains("bpmReference")') && $('.name:contains("bpmReference")').parent()) { const ref = $('.name:contains("bpmReference")').parent().select('.widget').children().eq(1); ref.append("<img src='./save.svg' style='height:20px; margin: 2px; cursor: pointer;' onClick='window.bpmreference(\"save\")'>"); ref.append("<img src='./open.svg' style='height:20px; margin: 2px; cursor: pointer;' onClick='window.bpmreference(\"open\")'>"); } } } if (lattice[i].envelope) {envelopeMenu(params, gui, i);} } } if (document.location.search.indexOf('servicearea')>-1 && document.location.search.indexOf('servicearea=')==-1) { for (let i in lattice) { if (document.location.search.indexOf(i)==-1); } } machineFolder.close(); if (Object.keys(blmfacilities).length>1) {blmres.menu(lattice, blmfacilities, params, gui, blm, componentCreator);} const highlightFolder = gui.addFolder('highlight components'); for (let j in componentCreator) { if (j.indexOf('Update')>-1) continue; const k = 'highlight_'+j; params[k] = typeof highlight == 'object' && highlight.includes(j); highlightFolder.add(params, k).name(j).onChange(mytoggle); } highlightFolder.close(); if (document.location.search.indexOf('highlight=')>-1) { gui.add(params, 'highlightScale', 0.1, 0.8, 0.01).onFinishChange(event => {toggleParam('highlightScale',event);}); } $('#ui-id-1').css('z-index', 10000000); if (document.location.search.indexOf('highlight=')>-1) {$('<div><iframe style="width: 100%;height:250px;" src="./spa/gauge.html?dark&r2only=1&r=100&ringwidth=58&max=1&throttlingPeriod=50&apply=shrink&val='+params.highlightShrink+'"></iframe></div>').insertBefore('.function');} for (let i in lattice) {if (i!='conf') initLattice(lattice[i].sections, i);} if (vlv) { 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 name = vlvSrc[j].split('/')[3]; if (vlvs[i].name.indexOf(name)>-1) {vlvs[i].vlvsrc = vlv; vlvs[i].vlvindex = j;} // conf.bstmap.base if (vlvSrc[j].split('/')[2]=="ACCESS_CONTROL" && i>39 && i<41) console.log('bst', j, vlvSrc[j], conf.bstmap.base, vlvs[i].name); if (vlvSrc[j].indexOf('/')>-1 && 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;} } } } console.log('vlvSrc', data, vlvSrc, conf.bstmap, vlvs); }) .catch(error => {console.log("Fetch error", error);}); setInterval(updateVlv, 1000); } if (document.location.search.indexOf('search=')>-1) findComponent(document.location.search.split('search=')[1].split('&')[0], false); if (lattice.conf && lattice.conf.shortcut["3d"]) initShortcut(lattice); if (lattice.conf && lattice.conf.index) initIndex(lattice); console.log(lattice, facilities); if (ps) { fetch(conf.stateSrcUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const statSrc = data.toUpperCase().split(','); for (let j=0; j<statSrc.length; j++) { if (typeof statSrc[j].split('/')[3] != 'undefined') statSrc[j] = statSrc[j].split('/')[3].replace('PS', ''); // if (statSrc[j].split('/')[1]=='POWER_SUPPLY') statSrc[j] = statSrc[j].split('/')[2].replace('PS', ''); } for (let i=0; i<status.length; i++) { for (let j=0; j<statSrc.length; j++) { if (status[i].name.indexOf(statSrc[j])>-1) {status[i].statsrc = ps; status[i].statindex = j;} } } console.log('statSrc', statSrc, status); }) .catch(error => {console.log('stateSrcUrl: '+conf.stateSrcUrl, "Fetch error", error);}); if (document.location.search.indexOf('nostats')==-1) { $('#notice').show(); $('#notice').css('color', 'orange'); const stats = "if (document.location.host.indexOf('mac3d')==-1) document.location = 'https://puma-01.elettra.eu/stat.php'"; let table = '<table style="font-size: 60%"><tr><td colspan="4" style="font-size: 150%" id="lt"></td></tr>'; table = table + '<tr><td style="width: 50%;" title="Milliseconds required by remote data retrival">last refresh '; table = table + '<button id="fetchError" style="background-color: red;display: inline-block;max-width: 0.8em; border-radius: 0.8em; max-height: 1em;" onClick="'+stats+'"> </button></td>'; table = table + '<td style="width: 50%; text-align: right;" id="requiredt"></td></tr><tr><td style="width: 50%;" title="Milliseconds between data timestamp and local timestamp (can be affected by local time offset)">delay</td>'; table = table + '<td style="width: 50%; text-align: right;" id="delay"></td></tr></table>'; $('#notice').html(table); } setInterval(updateStatus, 1000); } if (lattice.conf && lattice.conf.modules) { for (let i=0; i<lattice.conf.modules.length; i++) { const mod = lattice.conf.modules[i]; import(mod).then((modInst) => { console.log(modInst, mod); modInst[mod](lattice, params, compData, gui, conf, facilities); menuLink(); }); // window[lattice.conf.modules[i]](lattice, params, compData); } } else menuLink(); }) .catch(error => {console.log("Fetch error", error);}); function menuLink() { params.gotoAdmin = function() {document.location = './admin.php';}; gui.add(params, 'gotoAdmin').name('Admin'); params.goto2D = function() {document.location = './panther2d.php?machine='+params.machine;}; gui.add(params, 'goto2D').name('2D'); } function showStatus(i, stat) { status[i].scale.x = 1; status[i].scale.y = 1; status[i].scale.z = 1; if (stat == 'null' || stat == '' || stat == 0 || (!fel1 && status[i].facility=='fel1') || (!fel2 && status[i].facility=='fel2')) {status[i].visible = false;} else if (stat == 'ON' || stat == 'RUNNING') { status[i].visible = true; status[i].material.color.set(conf.stateLabelColor[stat]); status[i].scale.x = statScale; status[i].scale.y = statScale; status[i].scale.z = statScale; } else {status[i].visible = true; status[i].material.color.set(conf.stateLabelColor[stat]);} // if (i==40) console.log(i, status[i], stat); } function clearStatus() { for (let i=0; i<status.length; i++) { if (status[i].statsrc==ps && statVal[status[i].statindex]!=='ON' && statVal[status[i].statindex]!=='RUNNING') showStatus(i, 0); } } let statVal; const statScale = 0.07; let st, psDataTs, fetchError; function pad(n, width, z) { z = z || '0'; n = n + ''; return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; } function updateStatus() { st = new Date(); $('#lt').html(st.getFullYear()+'-'+pad(st.getMonth()+1, 2)+'-'+pad(st.getDate(), 2)+' '+pad(st.getHours(), 2)+':'+pad(st.getMinutes(), 2)+':'+pad(st.getSeconds(), 2)); fetch(conf.stateUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { // console.log('updateStatus()', fel1, fel2, status); statVal = data.split(';'); for (let i=0; i<status.length; i++) { if (status[i].statsrc==ps) { if (status[i].statindex) showStatus(i, statVal[status[i].statindex]); // if (status[i].name.indexOf('PSCH_B25')>-1) console.log(statVal, status, status[i].name, i, 'statindex: '+status[i].statindex, 'statVal: '+statVal[status[i].statindex]); } } if (document.location.search.indexOf('nostats')==-1) { console.log('fetch(eventData)',data); const lt = new Date(); const requiredt = new Date() - st; psDataTs = data.split(':')[0] - 0; $('#requiredt').html(requiredt); $('#delay').html(lt - psDataTs); } setTimeout(clearStatus, 600); fetchError = 0; $('#fetchError').hide(); }) .catch(error => { console.log("Fetch error", error); fetchError++; if (fetchError>3) {$('#fetchError').show();} }); } for (let i=0; i<envelopeNum; i++) { envelopeSize[i] = 500;} function updateEnvelopeCenters(data) { for (let i=0; i<envelopeMesh.length; i++) { x1 = (i+envelopeOffset) % envelopeNum; x2 = (i+envelopeOffset+1) % envelopeNum; const direction = false; //!envelopeMesh[i].mydirection; const width = envelopeMesh[i].width; envelopeMesh[i].geometry.dispose(); const envelopeGeometry = ellipticaltubeGeometry(envelopeFactorX*params.envelopeZoomX*data[0][direction? x2: x1],envelopeFactorY*params.envelopeZoomY*data[1][direction? x2: x1], envelopeFactorX*params.envelopeZoomX*data[0][direction? x1: x2],envelopeFactorY*params.envelopeZoomY*data[1][direction? x1: x2], width); envelopeMesh[i].geometry = envelopeGeometry.clone(); envelopeMesh[i].geometry.computeVertexNormals(); envelopeMesh[i].geometry.normalsNeedUpdate = true; envelopeMesh[i].geometry.verticesNeedUpdate = true; envelopeMesh[i].geometry.dynamic = true; } data = null; } function envelopeMenu(params, gui, i) { envelopeSrc = lattice[i].envelope.src; params.envelope = document.location.search.indexOf('envelope')>-1; gui.add(params, 'envelope').onChange(function() {toggleParam('envelope');}); if (document.location.search.indexOf('envelope')>-1) { params.envelopeZoomX = 1; gui.add(params, 'envelopeZoomX', 0.1, 10); params.envelopeZoomY = 1; gui.add(params, 'envelopeZoomY', 0.1, 10); if (document.location.search.indexOf('envelope=debug')>-1) {params.envelopeDebug = 0; gui.add(params, 'envelopeDebug', 0, envelopeNum-1, 1);} } } function readEnvelope() { // console.log('envelopeSrc', envelopeSrc); if (document.location.search.indexOf('envelope=debug')>-1) { const arr = Array(envelopeNum).fill(0.00001); arr[params.envelopeDebug] = 0.00015; updateEnvelopeCenters([arr, arr]); } else fetch(envelopeSrc) .then((response) => {return response.json();}) .then((eventData) => { updateEnvelopeCenters(eventData); }) .catch(error => {console.log("Fetch error", error);}); } if (document.location.search.indexOf('envelope')>-1) { readEnvelope(); setInterval(readEnvelope, 1200); } function appendEnvelope(facility, i, distanceFromBendingCenter, width, size1, size2, tang, direction) { if (tang==-Infinity) tang = -1e12; if (tang==Infinity) tang = 1e12; const x1 = i; const z1 = i+1; const d = distanceFromBendingCenter; const den = Math.sqrt(tang*tang+1) * direction; const envelopeGeometry = ellipticaltubeGeometry(size1,size2, size1,size2, width); envelopeMesh[envelopeIndex] = new THREE.Mesh(envelopeGeometry, envelopeMaterial); envelopeMesh[envelopeIndex].mydirection = direction>0; envelopeMesh[envelopeIndex].width = width; envelopeMesh[envelopeIndex].position.set(lattice[facility].sections[i].start.x + tang*d / den, Ydefault, lattice[facility].sections[i].start.z + d / den); envelopeMesh[envelopeIndex].rotateY(Math.atan(tang)); facilities[facility].add(envelopeMesh[envelopeIndex]); envelopeIndex++; } function envelopeAdd(flattice, i, j, facility, direction) { if (document.location.search.indexOf('envelope')>-1 && flattice[j].bending && flattice[j].bending.lengthIndex && lattice[facility].envelope) { let envelopePos = 0; const stop = j==23? flattice[23].bending.lengthIndex + envelopeNum: flattice[j].bending.lengthIndex; for (let e=flattice[(j+23)%24].bending.lengthIndex; e<stop; e++) { let envelopeLen = lattice[facility].envelope.length[e % envelopeNum]; appendEnvelope(facility, i, envelopePos, envelopeLen*direction, envelopeSize[envelopeCounter+1], envelopeSize[envelopeCounter], tang, direction); envelopeCounter++; envelopePos += envelopeLen * (e==flattice[j].bending.lengthIndex? 0.5: 1); } } } // status const normalMaterial = new THREE.MeshBasicMaterial({ color: 0xffff66}); const sphereGeometry = new THREE.SphereGeometry(500, 10, 10); const rot = {x: $('#rotx').val(), y: $('#roty').val(), z: $('#rotz').val()}; let gltfscene; function pushComponent(facility, i, m, tang, direction, y=Ydefault) { let magnet = m>-1? lattice[facility].sections[i].components[m].type: 'dipole'; if (typeof compBuffer[magnet] == 'undefined') compBuffer[magnet] = []; compBuffer[magnet].push({facility: facility, i: i, m: m, tang: tang, direction: direction, y: y}); } function placemagnet(magnet, gs) { const confIndex = magnet.indexOf('_stl')>-1? 'stl': (magnet.indexOf('_high')>-1? 'high': 'real'); console.log(magnet, conf[confIndex][magnet]); magnet = magnet.replace('_stl', '').replace('_high', ''); const gltfscene = new THREE.Object3D(); gs.scale.set(900, 900, 900); gs.rotateX(conf[confIndex][magnet].rot.x); gs.rotateY(conf[confIndex][magnet].rot.y); gs.rotateZ(conf[confIndex][magnet].rot.z); gs.position.set(conf[confIndex][magnet].pos.x, conf[confIndex][magnet].pos.y, conf[confIndex][magnet].pos.z); if (typeof conf[confIndex][magnet].zoom != 'undefined') { gs.scale.set(conf[confIndex][magnet].zoom, conf[confIndex][magnet].zoom,conf[confIndex][magnet].zoom);} gltfscene.add(gs); console.log('placemagnet', gltfscene, gltfscene.rotation, gltfscene.rotation.x, gltfscene.rotation.z); for (let l=0; l<compBuffer[magnet].length; l++) { const mycomp = gltfscene.clone(); const facility = compBuffer[magnet][l].facility; const i = compBuffer[magnet][l].i; const m = compBuffer[magnet][l].m; const tang = compBuffer[magnet][l].tang; const direction = compBuffer[magnet][l].direction; const y = compBuffer[magnet][l].y; const myname = m==-1? lattice[facility].sections[i].bending.name: lattice[facility].sections[i].components[m].name; // console.log(facility, i, m, myname); const id = extractId(myname); const d = m==-1? 0: lattice[facility].sections[i].components[m].position; const den = Math.sqrt(tang*tang+1) * direction; const offset = m>-1 && lattice[facility].sections[i].components[m].offset3d? lattice[facility].sections[i].components[m].offset3d: [0, 0, 0]; mycomp.position.set(params.highlightScale*(lattice[facility].sections[i].start.x + tang*d / den) + offset[0], y + offset[1], params.highlightScale*(lattice[facility].sections[i].start.z + d / den + offset[2])); if (mycomp.rotatedX) {mycomp.rotateZ(Math.atan(-tang));} else mycomp.rotateY(Math.atan(tang)); window.names.push(myname); if (m>-1 && lattice[facility].sections[i].components[m].embedded) { // console.log('mycomp.embedded',lattice[facility].sections[i].components[m].embedded); for (let j=0; j<lattice[facility].sections[i].components[m].embedded.length; j++) {names.push(lattice[facility].sections[i].components[m].embedded[j]); alias.push([myname,lattice[facility].sections[i].components[m].embedded[j]]);} } if (id[1]) {window.names.push(id[1]); alias.push(id);} mycomp.magnetType = magnet; mycomp.name = myname; if (direction>0) mycomp.rotateY(Math.PI); compBuffer[magnet][l].name = mycomp.name; if (m>-1 && lattice[facility].sections[i].components[m].href) mycomp.href = lattice[facility].sections[i].components[m].href; if (highlight.length && !highlight.find(element => element == magnet)) {mycomp.scale.set(params.highlightShrink, params.highlightShrink, params.highlightShrink);} facilities[facility].add(mycomp); compBuffer[magnet][l].mycomp = mycomp; scene.add(mycomp); } } function loadreal(magnet) { loader.load( './components/'+magnet+'.glb', function ( gltf ) { placemagnet(magnet, gltf.scene); }, undefined, function ( error ) { console.log(magnet); console.error( error ); }); } function appendComponent(facility, i, m, tang, direction, y=Ydefault) { if (tang==-Infinity) tang = -1e12; if (tang==Infinity) tang = 1e12; let magnet = lattice[facility].sections[i].components[m].type; // if (magnet=='quadrupole') return; // if (glb.indexOf(magnet)>-1) return; if (real && typeof conf.real[magnet] !== 'undefined') return; if (stl && typeof conf.stl[magnet] !== 'undefined') return; if (high && typeof conf.high[magnet] !== 'undefined') return; if (componentCreator[magnet+'fast'] && (fast==false || fast.indexOf(magnet)>-1)) magnet = magnet+'fast'; if (componentCreator[magnet+'premium'] && (premium==false || premium.indexOf(magnet)>-1)) magnet = magnet+'premium'; if (componentCreator[magnet]) { const myname = lattice[facility].sections[i].components[m].name; const mycomp = componentCreator[magnet](lattice[facility].sections[i].components[m]); mycomp.name = lattice[facility].sections[i].components[m].name; const id = extractId(myname); const d = lattice[facility].sections[i].components[m].position; const den = Math.sqrt(tang*tang+1) * direction; const offset = lattice[facility].sections[i].components[m].offset3d? lattice[facility].sections[i].components[m].offset3d: [0, 0, 0]; mycomp.position.set(params.highlightScale*(lattice[facility].sections[i].start.x + tang*d / den) + offset[0], y + offset[1], params.highlightScale*(lattice[facility].sections[i].start.z + d / den + offset[2])); if (mycomp.rotatedX) {mycomp.rotateZ(Math.atan(-tang));} else mycomp.rotateY(Math.atan(tang)); window.names.push(lattice[facility].sections[i].components[m].name); if (lattice[facility].sections[i].components[m].embedded) { // console.log('mycomp.embedded',lattice[facility].sections[i].components[m].embedded); for (let j=0; j<lattice[facility].sections[i].components[m].embedded.length; j++) {names.push(lattice[facility].sections[i].components[m].embedded[j]); alias.push([myname,lattice[facility].sections[i].components[m].embedded[j]]);} } if (id[1]) {window.names.push(id[1]); alias.push(id);} mycomp.magnetType = magnet; if (lattice[facility].sections[i].components[m].href) mycomp.href = lattice[facility].sections[i].components[m].href; if (highlight.length && !highlight.find(element => element == magnet)) {mycomp.scale.set(params.highlightShrink, params.highlightShrink, params.highlightShrink);} facilities[facility].add(mycomp); if (magnet=='blm') {blmres.append(blm, facility, mycomp, direction);} if (magnet=='vlv') {vlvs.push({'comp': mycomp, 'name': mycomp.name, 'type': magnet});} if (magnet=='bst') {vlvs.push({'comp': mycomp, 'name': mycomp.name, 'type': magnet});} if (Math.abs(den)>0.00001 && document.location.search.indexOf('nops')==-1) { if (mycomp.name.indexOf('CHV_S')>-1) console.log(mycomp, i, m, lattice[facility].sections[i].components[m]); if (typeof lattice[facility].sections[i].components[m].ps == "object") { for (let pi=0; pi<lattice[facility].sections[i].components[m].ps.length; pi++) { const sphere = new THREE.Mesh(sphereGeometry,normalMaterial.clone()); sphere.position.set(params.highlightScale*(lattice[facility].sections[i].start.x + tang*d / den) - 50 + 50*pi, y+500, params.highlightScale*(lattice[facility].sections[i].start.z + d / den)); sphere.name = lattice[facility].sections[i].components[m].ps[pi].replace('PS','') + '_status'; sphere.facility = facility; sphere.visible = false; status.push(sphere); facilities[facility].add(sphere); } } else { const sphere = new THREE.Mesh(sphereGeometry,normalMaterial.clone()); sphere.position.set(params.highlightScale*(lattice[facility].sections[i].start.x + tang*d / den), y+500, params.highlightScale*(lattice[facility].sections[i].start.z + d / den)); sphere.name = mycomp.name+'_status'; sphere.facility = facility; sphere.visible = false; status.push(sphere); facilities[facility].add(sphere); } } return mycomp.position; } } function extractId(name) { if (name.indexOf('(')>-1) { let tok = name.split('('); return [tok[0].replaceAll('.','_').replaceAll(' ','_'), tok[1].replaceAll('.','_').replaceAll(' ','_').replaceAll(')','')]; } return [name.replaceAll('.','_').replaceAll(' ','_'), false]; } function initLattice(flattice, facility) { // console.log('initLattice()', flattice, facility); compData[facility] = []; if (lattice[facility] && lattice[facility].bpm) {bpmPoints[facility] = []; bpmSkip[facility] = []; bpmData[facility] = []; } console.log('compData', compData, facility); facilities[facility] = new THREE.Object3D(); const wallColor = lattice.conf.wallColor? lattice.conf.wallColor: 'lightgray'; for (let i=0; i<flattice.length; i++) { if (document.location.search.indexOf('maxlatticenodes=')>-1 && document.location.search.split('maxlatticenodes=')[1].split('&')[0]<latticenodes) break; else latticenodes++; let j = (i+1) % flattice.length; let k = (i+2) % flattice.length; const y = typeof flattice[j].start.y == 'undefined'? Ydefault: flattice[j].start.y + Ydefault; tang = (flattice[j].start.x-flattice[i].start.x)/(flattice[j].start.z-flattice[i].start.z); const segmentLen = params.highlightScale*Math.sqrt((flattice[j].start.x-flattice[i].start.x)*(flattice[j].start.x-flattice[i].start.x) + (flattice[j].start.z-flattice[i].start.z)*(flattice[j].start.z-flattice[i].start.z)); if (flattice[j].chamber && (flattice[j].chamber.type=='chamber' || flattice[j].chamber.type=='wall')) { const chamberMesh = flattice[j].chamber.type=='chamber'? chamber(segmentLen+15): wall(segmentLen+15, wallColor); const yy = typeof flattice[i].start.y=='undefined' || typeof flattice[j].start.y=='undefined'? Ydefault: params.highlightScale*(flattice[j].start.y+flattice[i].start.y)/2; // if (flattice[j].chamber.type=='wall') console.log(flattice[j].start, yy, Ydefault); chamberMesh.position.set(params.highlightScale*(flattice[j].start.x+flattice[i].start.x)/2, yy, params.highlightScale*(flattice[j].start.z+flattice[i].start.z)/2); if (chamberMesh.rotatedX) {chamberMesh.rotateZ(Math.atan(-tang));} else chamberMesh.rotateY(Math.atan(tang)); chamberMesh.scale.set(params.highlightScale, 1, params.highlightScale); chamberMesh.magnetType = flattice[j].chamber.type; facilities[facility].add(chamberMesh); } let bendingType = flattice[j].bending? flattice[j].bending.type: ''; if (componentCreator[bendingType+'fast'] && (fast==false || fast.indexOf(bendingType)>-1)) bendingType = bendingType+'fast'; if (componentCreator[bendingType+'premium'] && (premium==false || premium.indexOf(bendingType)>-1)) bendingType = bendingType+'premium'; const direction = (facility=='servicearea'? 1: 1)*((flattice[j].start.z<flattice[i].start.z /*!= flattice[j].start.x<=flattice[i].start.x*/)? -1: 1); if (document.location.search.indexOf('components=hide')==-1 && flattice[j].bending && bendingType && typeof componentCreator[bendingType]!== 'undefined') { if (real && typeof conf.real[bendingType] !== 'undefined') { pushComponent(facility, i, -1, tang, direction, y); } else if (stl && typeof conf.stl[bendingType] !== 'undefined') { pushComponent(facility, i, -1, tang, direction, y); } else if (high && typeof conf.high[bendingType] !== 'undefined') { pushComponent(facility, i, -1, tang, direction, y); } else { component[dipoleNum] = flattice[j].bending.length? componentCreator[bendingType](flattice[j].bending.length): componentCreator[bendingType](); component[dipoleNum].position.set(params.highlightScale*flattice[j].start.x, y-params.highlightScale*100, params.highlightScale*flattice[j].start.z); component[dipoleNum].name = flattice[j].bending.name; const a1 = Math.atan2((flattice[j].start.z-flattice[i].start.z), (flattice[j].start.x-flattice[i].start.x)); const a2 = Math.atan2((flattice[k].start.z-flattice[j].start.z),(flattice[k].start.x-flattice[j].start.x)); alpha = (a1 + a2)/2; if (a1>0 && a2<0) alpha += Math.PI; component[dipoleNum].rotateY(3*Math.PI/2-alpha); if (highlight.length && !highlight.find(element => (element == 'bending' || element == bendingType))) {component[dipoleNum].scale.set(params.highlightShrink, params.highlightShrink, params.highlightShrink);} component[dipoleNum].magnetType = bendingType; facilities[facility].add(component[dipoleNum]); if (1 || flattice[j].bending.label && flattice[j].bending.label=='show') { const div = document.createElement('div'); facilities[facility].add(component[dipoleNum]); div.id = 'B' + dipoleNum; // 'B'+i; // flattice[j].bending.name; // div.classList = [facility]; div.style.cssText = 'position: absolute; color: white;'; div.innerHTML = flattice[j].bending.name; document.body.appendChild(div); dipoleNum++; } } window.names.push(flattice[j].bending.name); } if (document.location.search.indexOf('components=hide')==-1) if (flattice[i].components) for (let m=0; m<flattice[i].components.length; m++) { // if (facility=='servicearea') console.log(facility, i, m, tang, direction); const position = appendComponent(facility, i, m, tang, direction, y); // console.log(position, compData, facility); // if (position) compData[facility].push([position.clone(), Math.atan(tang), direction]); if (position) compData[facility].push([position.clone(), Math.atan2(flattice[j].start.x-flattice[i].start.x, flattice[j].start.z-flattice[i].start.z), direction]); pushComponent(facility, i, m, tang, direction, y); if (flattice[i].components[m].type=='bpm' && (document.location.search.indexOf('&bpm')>-1 || document.location.search.indexOf('?bpm')>-1) && lattice[facility].bpm) { if (lattice[facility].bpm.skip && (lattice[facility].bpm.skip.indexOf(flattice[i].components[m].name)>-1)) {bpmSkip[facility].push(bpmPoints[facility].length);} else bpmPoints[facility].push([position.clone(), Math.atan(tang), direction]); } } envelopeAdd(flattice, i, j, facility, direction); } // console.log('compBuffer', compBuffer); if (real) for (let i in conf.real) {loadreal(i);} if (stl) for (let i in conf.stl) {loadreal(i+'_stl');} if (high) for (let i in conf.high) {loadreal(i+'_high');} // console.log('vlvs', vlvs); console.log('facilities', facilities, 'names', window.names.join(';')); bpmAdd(facility); facilities[facility].name = facility; scene.add(facilities[facility]); // console.log('mytoggleFacility()', facility, params[facility], 'names', names); if (!params[facility]) mytoggleFacility(false, facility); // if (params.tour) setTimeout(switchTour, 2000); } function updateVlv() { fetch(conf.vlvUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const vlvVal = data.split(';'); vlvVal[0] = vlvVal[0].split(':')[1]; // console.log(conf.vlvUrl, vlvVal, vlvs); for (let i=0; i<vlvs.length; i++) { if (vlvs[i].type=='vlv') componentCreator.vlvUpdate(vlvs[i].comp, 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(','); componentCreator.bstUpdate(vlvs[i].comp, val[0]=='true'? 'limegreen': (val[1]=='true'? 'red': 'grey')); } } }) .catch(error => {console.log("Fetch error", error);}); } let cameraStep=0; let cameraUpdate = null; let focusPos = new THREE.Vector3(); const numSteps = 15; function stepCamera(x, y, z, rx, ry, rz) { if (debugcamera) {$('#x').val(x); $('#y').val(y); $('#z').val(z); $('#cx').val(rx); $('#cy').val(ry); $('#cz').val(rz);} camera.position.set(x, y, z); camera.lookAt(rx, ry, rz); // controls.target.set(x, y, z); // scene.rotation.set(rx, ry, rz); // camera.updateProjectionMatrix(); renderer.render(scene, camera); // composer.render(); } function updateCameraMatrix() { camera.updateProjectionMatrix(); scene.updateMatrixWorld(); console.log('updateCameraMatrix()', camera); } function updateCamera() { cameraStep = cameraStep>3? cameraStep-1: (cameraStep>1? cameraStep-0.5: cameraStep-0.25); stepCamera(x2-(x2-x1)*cameraStep/numSteps, y2-(y2-y1)*cameraStep/numSteps, z2-(z2-z1)*cameraStep/numSteps, rx2-(rx2-rx1)*cameraStep/numSteps, ry2-(ry2-ry1)*cameraStep/numSteps, rz2-(rz2-rz1)*cameraStep/numSteps); // console.log('updateCamera()', cameraStep, numSteps, camera.rotation, camera); if (cameraStep<=0) {clearInterval(cameraUpdate); cameraUpdate = null; setTimeout(updateCameraMatrix, 2000);} } export function findComponent(name, outline=true) { if (cameraStep>0) return; 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]; document.getElementById('sname').value = name; outlinePass.selectedObjects = []; for (let facility in scene.children) { for (let comp in scene.children[facility].children) { if (scene.children[facility].children[comp].name==name) { const dist = scene.children[facility].name=='servicearea'? 7000: 1000; facilities[scene.children[facility].name].visible = true; // make facility visible // transverse lil menu and check facility checkbox const menu = $('.allow-touch-styles').children(); for (let i=0; i<menu.length; i++) { if (menu[i].innerHTML=='toggle facility') { // $('.allow-touch-styles').children().eq(3).children().eq(4).children().eq(1).children().attr('checked', true) const fac = menu.eq(i+1).children(); for (let j=0; j<fac.length; j++) { if (fac.eq(j).children()[0].innerHTML==scene.children[facility].name) { fac.eq(j).children().eq(1).children().attr('checked', true); break; } } break; } } found = true; const selectedObject = scene.children[facility].children[comp]; if (outline) {outlinePass.selectedObjects = [selectedObject]; addSelectedObject(selectedObject);} renderer.render(scene, camera); x1 = camera.position.x; y1 = camera.position.y; z1 = camera.position.z; x2 = scene.children[facility].children[comp].position.x+dist*Math.sign(scene.children[facility].children[comp].position.x); y2 = scene.children[facility].children[comp].position.y+dist*Math.sign(scene.children[facility].children[comp].position.y); z2 = scene.children[facility].children[comp].position.z+dist*Math.sign(scene.children[facility].children[comp].position.z); console.log("findComponent()", name, selectedObject, x1, x2, scene.children[facility].children[comp].position, scene.children[facility]); const lookat = (new THREE.Vector3(0, 0, -20000)).applyQuaternion(camera.quaternion).add(camera.position); // https://stackoverflow.com/questions/27957645/three-js-find-the-current-lookat-of-a-camera rx1 = lookat.x; ry1 = lookat.y; rz1 = lookat.z; rx2 = scene.children[facility].children[comp].position.x; ry2 = scene.children[facility].children[comp].position.y; rz2 = scene.children[facility].children[comp].position.z; if (debugcamera) {$('#x').val(x1); $('#y').val(y1); $('#z').val(z1); $('#cx').val(rx1); $('#cy').val(ry1); $('#cz').val(rz1);} cameraStep = numSteps; updateCamera(); if (cameraUpdate == null) cameraUpdate = setInterval(updateCamera, 100); } } } } window.findComponent = findComponent; $(function() {$(".sname").autocomplete({source: names, close: function(event, ui) {findComponent($('#sname').val()); }, select: function(event, ui) {findComponent(ui.item.value); return false;}});}); function initVar(name, defaultVal) { return document.location.search.indexOf(name)>-1? document.location.search.split(name+'=')[1].split('&')[0]: defaultVal; } const tourSpeed = 10; // speed [m/s] const tourPeriod = initVar('tourPeriod', 20); const alphaPerPeriod = 0.008; const tourBackets = initVar('tourBackets', 15); const tourBacketSize = initVar('tourBacketSize', 0.1); const tourBacketsDistance = initVar('tourBacketsDistance', 1500); const tourHight = initVar('tourHight', 2000); const tourTransverse = initVar('tourTransverse', 1.1); const backets = []; const showBackets = initVar('showBackets', false); function tourCamera(x, y, z, ry) { camera.position.set(x*tourTransverse, y, z*tourTransverse); camera.rotateY(ry); renderer.render(scene, camera); } function placeBacket(backet, facility, section, pos) { const ns = section+1>=lattice[facility].sections.length? 0: section+1; const bx = lattice[facility].sections[section].start.x; const bz = lattice[facility].sections[section].start.z; const ex = lattice[facility].sections[ns].start.x; const ez = lattice[facility].sections[ns].start.z; const d = Math.sqrt((ex - bx)*(ex - bx) + (ez - bz)*(ez - bz)); const bpos = pos+(backet+1)*tourBacketsDistance; if (bpos>d) { placeBacket(backet, facility, ns, pos-d); } else { backets[backet].position.set(bx + bpos/d * (ex - bx), 1200, bz + bpos/d * (ez - bz)); } } function tour(facility, section, pos, alpha) { if (typeof lattice[facility] != 'undefined') { const ns = section+1>=lattice[facility].sections.length? 0: section+1; const bx = lattice[facility].sections[section].start.x; const bz = lattice[facility].sections[section].start.z; const ex = lattice[facility].sections[ns].start.x; const ez = lattice[facility].sections[ns].start.z; const d = Math.sqrt((ex - bx)*(ex - bx) + (ez - bz)*(ez - bz)); if (showBackets) for (let backet=0; backet<tourBackets; backet++) { placeBacket(backet, facility, section, pos); } console.log(section, pos, alpha/Math.PI*180); const beta = alpha>alphaPerPeriod? alphaPerPeriod: alpha; if (alpha>alphaPerPeriod) alpha = alpha - alphaPerPeriod; else alpha = 0; tourCamera(bx + pos/d * (ex - bx), tourHight, bz + pos/d * (ez - bz), -beta); // -Math.PI/2 if (pos+tourSpeed*tourPeriod*9>=d && alpha==0) { const ns2 = ns+1>=lattice[facility].sections.length? 0: ns+1; alpha = Math.atan2(lattice[facility].sections[ns2].start.z - lattice[facility].sections[ns].start.z, lattice[facility].sections[ns2].start.x - lattice[facility].sections[ns].start.x) - Math.atan2(lattice[facility].sections[ns].start.z - lattice[facility].sections[section].start.z, lattice[facility].sections[ns].start.x - lattice[facility].sections[section].start.x); console.log(alpha/Math.PI*180); } if (pos+tourSpeed*tourPeriod>=d) { section = ns; pos = pos+tourSpeed*tourPeriod - d; } else pos = pos+tourSpeed*tourPeriod; if (params.tour) setTimeout(tour, tourPeriod, facility, section, pos, (alpha<0? 2*Math.PI+alpha: alpha)*0.6); } } function switchTour() { console.log(lattice); const facility = 'sr'; if (showBackets && backets.length==0) for (let pi=1; pi<=tourBackets; pi++) { const sphere = new THREE.Mesh(sphereGeometry,normalMaterial.clone()); const bx = lattice[facility].sections[0].start.x; const bz = lattice[facility].sections[0].start.z; const ex = lattice[facility].sections[1].start.x; const ez = lattice[facility].sections[1].start.z; const d = Math.sqrt((ex - bx)*(ex - bx) + (ez - bz)*(ez - bz)); sphere.position.set(bx + pi*tourBacketsDistance/d * (ex - bx), 1200, bz + pi*tourBacketsDistance/d * (ez - bz)); sphere.name = 'backet'+pi; sphere.scale.x = tourBacketSize; sphere.scale.y = tourBacketSize; sphere.scale.z = tourBacketSize; // sphere.visible = false; backets.push(sphere); facilities[facility].add(sphere); } const cameraPos = typeof conf.default_camera_init[machine] == 'undefined'? conf.default_camera_init.default: conf.default_camera_init[machine]; camera.position.set(cameraPos[0]*params.highlightScale, cameraPos[1]*params.highlightScale, cameraPos[2]*params.highlightScale); camera.rotation.set(cameraPos[3],cameraPos[4],cameraPos[5]); camera.rotateY(-Math.PI*1); if (params.tour) tour(facility, 0, 0, 0); } function addSelectedObject(object) { // console.log("addSelectedObject()", object, outlinePass.selectedObjects); selectedObjects = []; selectedObjects.push(object); } function checkOutline(selectedObject) { raycaster.setFromCamera(pointer, camera); addSelectedObject(selectedObject); outlinePass.selectedObjects = selectedObjects; } let popupOn = false; if (navigator.userAgent.indexOf('Firefox/63')==-1) document.onclick = handleMouseMove; if (navigator.userAgent.indexOf('Firefox/63')>-1 && document.location.search.indexOf('popup')>-1) document.addEventListener('click', handleClick); function handleClick(event) { // document.removeEventListener('click', handleClick); console.log(event); let j=1; const debug = document.location.search.indexOf('debug=')>-1? document.location.search.split('debug=')[1].split('&')[0].split(','): ''; if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), event',event); pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.y = - ((28+event.clientY) / (window.innerHeight)) * 2 + 1; if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), pointer',pointer); raycaster.setFromCamera(pointer, camera); const intersects = raycaster.intersectObjects(scene.children); if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), intersects',intersects); window.tooltipObject = tooltipObject = null; document.getElementById('tooltipFrame').src = document.getElementById('compdb').href = ''; document.getElementById('compdb').style.display = 'inline'; if (document.location.pathname.indexOf('ff63')==-1) document.getElementById('tooltip').style.display = 'none'; document.getElementById('tooltipFrame').width = '500px'; if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), tooltipObject',tooltipObject); if (!found) outlinePass.selectedObjects = selectedObjects = []; found = false; if (typeof intersects[0] == 'undefined') return; let selectedObject = intersects[0].object.parent; for (let i=intersects.length-1; i>=0; i--) { if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), i',i); let visible = true; let tobj = intersects[i].object; // https://discourse.threejs.org/t/how-to-check-if-mesh-ancestor-is-visible-in-canvas/42494/5 while (tobj !== scene) { if (tobj.visible == false) visible = false; tobj = tobj.parent; } if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), tobj',tobj); if (visible == false) continue; // console.log("handleMouseMove()", event, intersects[i].object); if (intersects[i].object.parent.name.length==0 && intersects[i].object.parent.parent.name.length==0) continue; if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), intersects[i].object',intersects[i].object); if (intersects[i].object.parent.href) { window.tooltipObject = tooltipObject = intersects[i].object.parent; document.getElementById('tooltipFrame').src = demo? './magnetdemo.html?'+intersects[i].object.parent.href.split('?')[1]: intersects[i].object.parent.href; document.getElementById('compdb').style.display = 'none'; } else if (intersects[i].object.href) { window.tooltipObject = tooltipObject = intersects[i].object; document.getElementById('tooltipFrame').src = demo? './magnetdemo.html?'+intersects[i].object.href.split('?')[1]: intersects[i].object.href; document.getElementById('compdb').href = conf.compdb + intersects[i].object.name; document.getElementById('compname').innerHTML = intersects[i].object.name; } else if (intersects[i].object.magnetType) { window.tooltipObject = tooltipObject = intersects[i].object; document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s='+(demo? 'knobdemo': intersects[i].object.magnetType.replace('fast', '').replace('premium', ''))+'¶m='+intersects[i].object.name; document.getElementById('compdb').href = conf.compdb+intersects[i].object.name; document.getElementById('compname').innerHTML = intersects[i].object.name; } else if (intersects[i].object.parent.magnetType) { window.tooltipObject = tooltipObject = intersects[i].object.parent; document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s='+(demo? 'knobdemo': intersects[i].object.parent.magnetType.replace('fast', '').replace('premium', ''))+'¶m='+intersects[i].object.parent.name; document.getElementById('compdb').href = conf.compdb+intersects[i].object.parent.name; document.getElementById('compname').innerHTML = intersects[i].object.parent.name; } else if (intersects[i].object.parent.parent.magnetType) { document.getElementById('tooltipFrame').height = '320px'; window.tooltipObject = tooltipObject = intersects[i].object.parent.parent; selectedObject = intersects[i].object.parent.parent; document.getElementById('tooltipFrame').src = conf.tooltipAppNormal+'?s='+(demo? 'knobdemo': intersects[i].object.parent.parent.magnetType.replace('fast', '').replace('premium', ''))+'¶m='+intersects[i].object.parent.parent.name; document.getElementById('compdb').href = conf.compdb+intersects[i].object.parent.parent.name; document.getElementById('compname').innerHTML = intersects[i].object.parent.parent.name; } else { window.tooltipObject = tooltipObject = intersects[i].object.parent; document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s=label¶m='+intersects[i].object.parent.name; document.getElementById('compdb').href = conf.compdb+intersects[i].object.parent.name; document.getElementById('compname').innerHTML = intersects[i].object.parent.name; } if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), src',document.getElementById('tooltipFrame').src, ', href',document.getElementById('compdb').href); const name = document.getElementById('compname').innerHTML; document.getElementById('compdb').removeAttribute("disabled"); if (name.indexOf('_B')>-1 || name.indexOf('_P')>-1 || (name.indexOf('_S')>-1 && name.indexOf('_SFEL')==-1 && name.indexOf('_SP')==-1 && name.indexOf('_SC')==-1)) document.getElementById('compdb').setAttribute("disabled", true); document.getElementById('compname').innerHTML = document.getElementById('compname').innerHTML.replace('RTBPM','BPM'); document.getElementById('compdb').href = document.getElementById('compdb').href.replace('RTBPM','BPM').split(' ')[0].split('%')[0]; if (document.getElementById('tooltipFrame').src.indexOf('?s=beamline')>-1) { document.getElementById('compdb').href = 'http://adam.elettra.trieste.it/projects/blcs/beamwatch/'; document.getElementById('compname').innerHTML = document.getElementById('compname').innerHTML + ' in ADAM Beamwatch'; } document.getElementById('tooltipFrame').src.replace('s=dipolefermi', 's=bending'); } if (document.getElementById('tooltipFrame').src.indexOf('s=wall')>-1) {document.getElementById('compdb').style.display = 'none'; return;} // avoid walls console.log('handleClick()', intersects, document.getElementById('tooltipFrame').src, selectedObject); // if (selectedObject.parent && selectedObject.parent.type && selectedObject.parent.type == "Scene") return; // avoid selecting everything checkOutline(selectedObject); renderer.render(scene, camera); if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), compdb',document.getElementById("compdb")); document.getElementById("compdb").addEventListener("click", compLink); // https://j11y.io/javascript/debug-jquery-events-with-listhandlers/ } function handleMouseMove(event) { let j=1; const debug = document.location.search.indexOf('debug=')>-1? document.location.search.split('debug=')[1].split('&')[0].split(','): ''; if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), event',event); pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.y = - ((28+event.clientY) / (window.innerHeight)) * 2 + 1; if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), pointer',pointer); raycaster.setFromCamera(pointer, camera); const intersects = raycaster.intersectObjects(scene.children); if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), intersects',intersects); window.tooltipObject = tooltipObject = null; document.getElementById('tooltipFrame').src = document.getElementById('compdb').href = ''; document.getElementById('compdb').style.display = 'inline'; if (document.location.pathname.indexOf('ff63')==-1) document.getElementById('tooltip').style.display = 'none'; document.getElementById('tooltipFrame').width = '500px'; if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), tooltipObject',tooltipObject); if (!found) outlinePass.selectedObjects = selectedObjects = []; found = false; if (typeof intersects[0] == 'undefined') return; let selectedObject = intersects[0].object.parent; for (let i=intersects.length-1; i>=0; i--) { if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), i',i); let visible = true; let tobj = intersects[i].object; // https://discourse.threejs.org/t/how-to-check-if-mesh-ancestor-is-visible-in-canvas/42494/5 while (tobj !== scene) { if (tobj.visible == false) visible = false; tobj = tobj.parent; } if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), tobj',tobj); if (visible == false) continue; // console.log("handleMouseMove()", event, intersects[i].object); if (intersects[i].object.parent.name.length==0 && intersects[i].object.parent.parent.name.length==0) continue; if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), intersects[i].object',intersects[i].object); if (intersects[i].object.parent.href) { window.tooltipObject = tooltipObject = intersects[i].object.parent; document.getElementById('tooltipFrame').src = demo? './magnetdemo.html?'+intersects[i].object.parent.href.split('?')[1]: intersects[i].object.parent.href; document.getElementById('compdb').style.display = 'none'; } else if (intersects[i].object.href) { window.tooltipObject = tooltipObject = intersects[i].object; document.getElementById('tooltipFrame').src = demo? './magnetdemo.html?'+intersects[i].object.href.split('?')[1]: intersects[i].object.href; document.getElementById('compdb').href = conf.compdb + intersects[i].object.name; document.getElementById('compname').innerHTML = intersects[i].object.name; } else if (intersects[i].object.magnetType) { window.tooltipObject = tooltipObject = intersects[i].object; document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s='+(demo? 'knobdemo': intersects[i].object.magnetType.replace('fast', '').replace('premium', ''))+'¶m='+intersects[i].object.name; document.getElementById('compdb').href = conf.compdb+intersects[i].object.name; document.getElementById('compname').innerHTML = intersects[i].object.name; } else if (intersects[i].object.parent.magnetType) { window.tooltipObject = tooltipObject = intersects[i].object.parent; document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s='+(demo? 'knobdemo': intersects[i].object.parent.magnetType.replace('fast', '').replace('premium', ''))+'¶m='+intersects[i].object.parent.name; document.getElementById('compdb').href = conf.compdb+intersects[i].object.parent.name; document.getElementById('compname').innerHTML = intersects[i].object.parent.name; } else if (intersects[i].object.parent.parent.magnetType) { document.getElementById('tooltipFrame').height = '320px'; window.tooltipObject = tooltipObject = intersects[i].object.parent.parent; selectedObject = intersects[i].object.parent.parent; document.getElementById('tooltipFrame').src = conf.tooltipAppNormal+'?s='+(demo? 'knobdemo': intersects[i].object.parent.parent.magnetType.replace('fast', '').replace('premium', ''))+'¶m='+intersects[i].object.parent.parent.name; document.getElementById('compdb').href = conf.compdb+intersects[i].object.parent.parent.name; document.getElementById('compname').innerHTML = intersects[i].object.parent.parent.name; } else { window.tooltipObject = tooltipObject = intersects[i].object.parent; document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s=label¶m='+intersects[i].object.parent.name; document.getElementById('compdb').href = conf.compdb+intersects[i].object.parent.name; document.getElementById('compname').innerHTML = intersects[i].object.parent.name; } if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), src',document.getElementById('tooltipFrame').src, ', href',document.getElementById('compdb').href); const name = document.getElementById('compname').innerHTML; document.getElementById('compdb').removeAttribute("disabled"); if (name.indexOf('_B')>-1 || name.indexOf('_P')>-1 || (name.indexOf('_S')>-1 && name.indexOf('_SFEL')==-1 && name.indexOf('_SP')==-1 && name.indexOf('_SC')==-1)) document.getElementById('compdb').setAttribute("disabled", true); document.getElementById('compname').innerHTML = document.getElementById('compname').innerHTML.replace('RTBPM','BPM'); document.getElementById('compdb').href = document.getElementById('compdb').href.replace('RTBPM','BPM').split(' ')[0].split('%')[0]; if (document.getElementById('tooltipFrame').src.indexOf('?s=beamline')>-1) { document.getElementById('compdb').href = 'http://adam.elettra.trieste.it/projects/blcs/beamwatch/'; document.getElementById('compname').innerHTML = document.getElementById('compname').innerHTML + ' in ADAM Beamwatch'; } document.getElementById('tooltipFrame').src.replace('s=dipolefermi', 's=bending'); } console.log('handleMouseMove()', intersects, document.getElementById('tooltipFrame').src); if (document.getElementById('tooltipFrame').src.indexOf('s=wall')>-1) {window.tooltipObject = tooltipObject = null; return;} // avoid walls console.log('handleMouseMove()', intersects, document.getElementById('tooltipFrame').src); if (selectedObject.parent && selectedObject.parent.type && selectedObject.parent.type == "Scene") return; // avoid selecting everything checkOutline(selectedObject); renderer.render(scene, camera); if (debug.indexOf(''+j++)>-1) console.log('handleMouseMove(), compdb',document.getElementById("compdb")); document.getElementById("compdb").addEventListener("click", compLink); // https://j11y.io/javascript/debug-jquery-events-with-listhandlers/ } animate(); function moveCamera() { const cameraInitPosition = typeof conf.default_camera_init[machine] == 'undefined'? conf.default_camera_init.default: conf.default_camera_init[machine]; // const cameraPos = (document.location.search.indexOf('camera=')>-1)? document.location.search.split('camera=')[1].split('&')[0].split(','): [-22000, 7200, 70000, -0.1, -0.3, -0.03]; const cameraPos = (document.location.search.indexOf('camera=')>-1)? document.location.search.split('camera=')[1].split('&')[0].split(','): cameraInitPosition; if (debugcamera) {$('#x').val(camera.position.x); $('#y').val(camera.position.y); $('#z').val(camera.position.z); $('#cx').val(camera.rotation.x); $('#cy').val(camera.rotation.y); $('#cz').val(camera.rotation.z);} if (cameraStarted) {if (debugcamera) setUrl('camera', camera.position.x+','+camera.position.y+','+camera.position.z+','+camera.rotation.x+','+camera.rotation.y+','+camera.rotation.z); return;} console.log('moveCamera()', camera.rotation, machine, cameraInitPosition); cameraStarted = true; camera.position.set(cameraPos[0]*params.highlightScale, cameraPos[1]*params.highlightScale, cameraPos[2]*params.highlightScale); camera.rotation.set(cameraPos[3],cameraPos[4],cameraPos[5]); } function setFar(far) { console.log('setFar', far); camera.far = far*1000; camera.updateProjectionMatrix(); } window.setFar = setFar; function init() { scene = new THREE.Scene(); scene.background = new THREE.Color(document.location.search.indexOf('background=')>-1? document.location.search.split('background=')[1].split('&')[0]: 0x777777); const canvas = document.querySelector('#c'); renderer = new THREE.WebGLRenderer({antialias: false, canvas, logarithmicDepthBuffer: true}); // renderer = new WebGPURenderer(); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const fov = document.location.search.indexOf('fov=')>-1? document.location.search.split('fov=')[1].split('&')[0]-0: 45; camera = new THREE.PerspectiveCamera(fov, window.innerWidth / window.innerHeight, 0.001, fast!==false? 1000000: 120000); // controls if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) controls = new MapControls(camera, renderer.domElement); else { controls = new OrbitControls(camera, renderer.domElement); controls.mouseButtons.LEFT = THREE.MOUSE.PAN; controls.mouseButtons.RIGHT = THREE.MOUSE.ROTATE; controls.keyPanSpeed = 1000; } console.log('controls', controls); // https://threejs.org/docs/#examples/en/controls/OrbitControls // https://stackoverflow.com/questions/61519164/three-js-orbitcontrols-panning-and-scene-rotation?rq=3 // https://github.com/pmndrs/drei/issues/186 // controls.enableDamping = true; controls.enableDamping = false; // an animation loop is required when either damping or auto-rotation are enabled controls.dampingFactor = 0.1; controls.screenSpacePanning = false; controls.minDistance = 0.01; controls.maxDistance = 300000; controls.maxPolarAngle = Math.PI / 2; // controls.update(); window.addEventListener('resize', onWindowResize); camera.updateMatrixWorld(true); setFar(params.far); const dirLight1 = new THREE.DirectionalLight(0xffffff, 3); dirLight1.position.set(1, 1, 1); scene.add(dirLight1); const dirLight2 = new THREE.DirectionalLight(0x888888, 3); dirLight2.position.set(-1, -1, -1); scene.add(dirLight2); const ambientLight = new THREE.AmbientLight(0xffffff, 2); scene.add(ambientLight); // postprocessing (derived from https://github.com/mrdoob/three.js/blob/master/examples/webgl_postprocessing_outline.html) composer = new EffectComposer(renderer); const renderPass = new RenderPass(scene, camera); composer.addPass(renderPass); outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera); outlinePass.edgeStrength = 100; outlinePass.edgeGlow = 0.2; outlinePass.edgeThickness = 6; outlinePass.pulsePeriod = 2; outlinePass.visibleEdgeColor.set('#700080'); outlinePass.hiddenEdgeColor.set('#400050'); composer.addPass(outlinePass); console.log('scene', scene, 'controls', controls, 'camera', camera); if (debugcamera) { // axis grid const redmaterial = new THREE.LineBasicMaterial({ color: 0xff0000 }); const redgeometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(-3000, 0, 0), new THREE.Vector3(3000, 0, 0)]); const redline = new THREE.Line( redgeometry, redmaterial ); scene.add(redline); for (let k=-30; k<=30; k++) { const c = redline.clone(); c.position.set(0, k*100, 0); scene.add(c); } const greenmaterial = new THREE.LineBasicMaterial({ color: 0x00ff00 }); const greengeometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, -3000, 0), new THREE.Vector3(0, 3000, 0)]); const greenline = new THREE.Line( greengeometry, greenmaterial ); scene.add(greenline); for (let k=-30; k<=30; k++) { const c = greenline.clone(); c.position.set(0, 0, k*100); scene.add(c); } const bluematerial = new THREE.LineBasicMaterial({ color: 0x0000ff }); const bluegeometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, -3000), new THREE.Vector3(0, 0, 3000)]); const blueline = new THREE.Line( bluegeometry, bluematerial ); scene.add(blueline); for (let k=-30; k<=30; k++) { const c = blueline.clone(); c.position.set(k*100, 0, 0); scene.add(c); } } } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function animate() { requestAnimationFrame(animate); if (document.location.search.indexOf('stat')>-1) stats.begin(); moveCamera(); // controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true render(); composer.render(); if (document.location.search.indexOf('stat')>-1) stats.end(); } function render() { const width = window.innerWidth, height = window.innerHeight; const widthHalf = width / 2, heightHalf = height / 2; if (dipoleNum>0) renderer.render(scene, camera); for (let dipoleIndex=0; dipoleIndex<dipoleNum; dipoleIndex++) if (typeof component[dipoleIndex] != 'undefined' && document.getElementById('B'+(dipoleIndex))) { const pos = component[dipoleIndex].position.clone(); pos.project(camera); const frustum = new THREE.Frustum(); const matrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); frustum.setFromProjectionMatrix(matrix); const dist = Math.abs(camera.position.x-component[dipoleIndex].position.x)+Math.abs(camera.position.y-component[dipoleIndex].position.y)+Math.abs(camera.position.z-component[dipoleIndex].position.z); const facility = document.getElementById('B'+(dipoleIndex)).className; document.getElementById('B'+(dipoleIndex)).style.display = (frustum.containsPoint(component[dipoleIndex].position) && facilities[facility].visible && dist>140 && dist<40000*params.highlightScale)? 'block': 'none'; document.getElementById('B'+(dipoleIndex)).style.top = (-(pos.y * heightHalf) + heightHalf - 20)+'px'; document.getElementById('B'+(dipoleIndex)).style.left = ((pos.x * widthHalf ) + widthHalf + 15)+'px'; } blmres.render(blm, params, componentCreator, camera, THREE); if (typeof tooltipObject !== 'undefined' && tooltipObject != null) { let pos2 = window.tooltipObject.position.clone(); pos2.project(camera); const frustum = new THREE.Frustum(); const matrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); frustum.setFromProjectionMatrix(matrix); let top = (-(pos2.y * heightHalf) + heightHalf ); if (window.innerHeight - top < 400) top = window.innerHeight - 400; if (document.location.pathname.indexOf('ff63')==-1) { document.getElementById('tooltip').style.top = top+'px'; document.getElementById('tooltip').style.left = ((pos2.x * widthHalf ) + widthHalf + 25)+'px'; document.getElementById('tooltip').style.display = (frustum.containsPoint(tooltipObject.position))? 'block': 'none'; } } } function updateUrl(param, value) { let url = document.location.href; if (url.indexOf(param)==-1) url = url + (url.indexOf('?')==-1? '?': '&') + param + '=' + value; else { const a = url.split(param+'='); const b = a[1].split('&'); url = a[0] + param + '=' + value + (b.length>1? '&' + b[1]: ''); } window.history.pushState({"html":'panther.php',"pageTitle":'PAnTHer'},"", url); } function mytoggleFacility(value, facility=false) { if (!facility) facility = this.property; console.log('facilities', facilities, facility); facilities[facility].visible = value; } export function toggleMachine(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); } export function toggleParam(name, value) { if (name=='backgroundColor') {scene.background = new THREE.Color(params.backgroundColor);return;} const urlparam = {}; let search = document.location.search.replace('?', '').split('&'); if (search[0]=='') search.splice(0,1); for (let i=0; i<search.length; i++) {const s = search[i].split('='); urlparam[s[0]] = s.length>1? s[1]: undefined;} if (name.indexOf('highlight_')>-1) { const hname = name.split('ighlight_')[1]; if (!highlight.includes(hname)) highlight.push(hname); else highlight.splice(highlight.indexOf(hname), 1); urlparam.highlight = highlight.join(','); search=[]; for (let i in urlparam) search.push(i+(typeof urlparam[i]!='undefined'? '='+urlparam[i]: '')); } else if (name.indexOf('highlight')>-1) { if (name=='highlightShrink') { updateUrl(name, params.highlightShrink); for (let i=0; i<scene.children.length; i++) { for (let j=0; j<scene.children[i].children.length; j++) { if (!highlight.find(element => element == scene.children[i].children[j].magnetType)) if (scene.children[i].children[j].magnetType != 'chamber') scene.children[i].children[j].scale.set(params.highlightShrink, params.highlightShrink, params.highlightShrink); } } return; } urlparam[name] =value; search=[]; for (let i in urlparam) search.push(i+(typeof urlparam[i]!='undefined'? '='+urlparam[i]: '')); } else if (search.includes(name)) { search.splice(search.indexOf(name), 1); } else { search.push(name); } if (search.indexOf("highlight=")>-1) {for (let j=search.length-1; j>=0; j--) if (search[j].indexOf("highlight")>-1) search.splice(j, 1);} if (name=='mode') { if (search.indexOf(value)>-1 || (value=='normal' && search.indexOf('fast')==-1 && search.indexOf('premium')==-1 && search.indexOf('plus')==-1)) return; search = search.filter(item => item !== 'fast' && item !== 'plus' && item !== 'premium' && item !== 'mode'); if (value!='normal') search.push(value); } const res = search.join('&'); document.location = document.location.href.split('?')[0]+(res.length? '?'+res: ''); } window.mytoggle = toggleParam; if (demo) { $('.lil-gui').css({'top': '120px', 'right': '1px'}); $( "body" ).append('<div class="lil-gui allow-touch-styles root autoPlace" style="top: 0px;right: 1px;height: 120px;background-color: black;"></div>'); } if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) gui.close();