Skip to content
Snippets Groups Projects
panther.js 76.4 KiB
Newer Older
Lucio Zambon's avatar
Lucio Zambon committed
// 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';
Lucio Zambon's avatar
Lucio Zambon committed
	import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
Lucio Zambon's avatar
Lucio Zambon committed
	/* 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';
	*/
Lucio Zambon's avatar
Lucio Zambon committed

	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();
Lucio Zambon's avatar
Lucio Zambon committed
	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];}
Lucio Zambon's avatar
Lucio Zambon committed
	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;
Lucio Zambon's avatar
Lucio Zambon committed
	const demo = document.location.search.indexOf('demo')>-1; // || document.location.search.indexOf('machine=elettra2')>-1;
Lucio Zambon's avatar
Lucio Zambon committed
	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 = {};
Lucio Zambon's avatar
Lucio Zambon committed
	const compData = {};
Lucio Zambon's avatar
Lucio Zambon committed
	const compBuffer = {};
	const loader = new GLTFLoader();
Lucio Zambon's avatar
Lucio Zambon committed
	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;
Lucio Zambon's avatar
Lucio Zambon committed
	const real = (document.location.search.indexOf('real')>-1 || (machine=='elettra2' && premium)) && document.location.search.indexOf('stl')==-1 && document.location.search.indexOf('high')==-1;
Lucio Zambon's avatar
Lucio Zambon committed
	const stl = document.location.search.indexOf('stl')>-1;
Lucio Zambon's avatar
Lucio Zambon committed
	const high = document.location.search.indexOf('high')>-1;
Lucio Zambon's avatar
Lucio Zambon committed
	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;
Lucio Zambon's avatar
Lucio Zambon committed
		})
		.catch(error => {console.log("Fetch error", error);});
Lucio Zambon's avatar
Lucio Zambon committed
	}
	const gui = new GUI();
	gui.add(params, 'machine', conf.machineList).onChange(function() {toggleMachine(params.machine);});
Lucio Zambon's avatar
Lucio Zambon committed
	params.mode = document.location.search.indexOf('fast')>-1? 'fast': document.location.search.indexOf('premium')>-1? 'premium': document.location.search.indexOf('plus')>-1? 'plus': 'normal';
Lucio Zambon's avatar
Lucio Zambon committed
	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');});
Lucio Zambon's avatar
Lucio Zambon committed

	const sstring = $('.controller.string').children().eq(1).children().eq(0);
	sstring.attr('id', 'sname');
	sstring.attr('name', 'sname');
	sstring.addClass("form-control sname");

Lucio Zambon's avatar
Lucio Zambon committed
	gui.addColor(params, 'backgroundColor').onChange(function() {toggleParam('backgroundColor');});
Lucio Zambon's avatar
Lucio Zambon committed
	params.far = typeof conf.default_far[machine]!='undefined'? conf.default_far[machine]: conf.default_far.default;
Lucio Zambon's avatar
Lucio Zambon committed
	gui.add(params, 'far', 50, 500).onChange(function() {setFar(params.far);});
Lucio Zambon's avatar
Lucio Zambon committed
	params.tour = document.location.search.indexOf('tour=')>-1;
Lucio Zambon's avatar
Lucio Zambon committed
	gui.add(params, 'tour').onChange(function() {switchTour();});
Lucio Zambon's avatar
Lucio Zambon committed
	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) {
Lucio Zambon's avatar
Lucio Zambon committed
		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);});
		}
Lucio Zambon's avatar
Lucio Zambon committed
	}
	window.bpmreference = bpm_reference;
	function updateBpm(data, facility) {
Lucio Zambon's avatar
Lucio Zambon committed
		console.log('updateBpm(), data:', data, ', facility:', facility, ', bpmMesh', bpmMesh);
Lucio Zambon's avatar
Lucio Zambon committed
		bpmData[facility] = data;
		bpmMesh[facility].geometry.dispose();
		bpmIndex[facility] = lattice[facility].bpm.offset;
		const path = bpmPath(data, facility);
Lucio Zambon's avatar
Lucio Zambon committed
		console.log('updateBpm(), data:', data, ', facility:', facility, ', bpmMesh', bpmMesh, 'path', path);
Lucio Zambon's avatar
Lucio Zambon committed
		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() {
Lucio Zambon's avatar
Lucio Zambon committed
		fetch(conf.bpmUrl)
		.then((response) => {return response.json();})
		.then((data) => {
			for (let facility in data) {
				updateBpm(data[facility], facility);
			}
Lucio Zambon's avatar
Lucio Zambon committed
			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);
			}
Lucio Zambon's avatar
Lucio Zambon committed
		})
Lucio Zambon's avatar
Lucio Zambon committed
		.catch(error => {console.log("Fetch error", error, conf.bpmUrl);});
Lucio Zambon's avatar
Lucio Zambon committed
	}
	function bpmPath(data, facility) {
		let len = lattice[facility].bpm.length - bpmSkip[facility].length;
		if (len>bpmPoints[facility].length) len = bpmPoints[facility].length;
Lucio Zambon's avatar
Lucio Zambon committed
		// if (data!=-1) console.log('bpmDbg', len, facility, data, bpmPoints[facility].length); 
Lucio Zambon's avatar
Lucio Zambon committed
		const bpmCurve = [];
Lucio Zambon's avatar
Lucio Zambon committed
		const bpmFactor = conf.bpmFactor[facility];
Lucio Zambon's avatar
Lucio Zambon committed
		for (let i=0; i<len; i++) {
Lucio Zambon's avatar
Lucio Zambon committed
			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);
Lucio Zambon's avatar
Lucio Zambon committed
			const ty = Ydefault + bpmPoints[facility][i][0].y + (data.Ver? data.Ver[j]*1000*bpmFactor*params.bpmZoomV: 0);
Lucio Zambon's avatar
Lucio Zambon committed
			const tz = bpmPoints[facility][i][0].z - Math.sin(bpmPoints[facility][i][1])*(data.Hor? data.Hor[j]*1000*bpmFactor*params.bpmZoomH: 0);
Lucio Zambon's avatar
Lucio Zambon committed
			if (!isNaN(tx) && !isNaN(ty) && !isNaN(tz)) bpmCurve.push(new THREE.Vector3(tx, ty, tz));
Lucio Zambon's avatar
Lucio Zambon committed
		}
Lucio Zambon's avatar
Lucio Zambon committed
		// console.log(facility, bpmCurve);
Lucio Zambon's avatar
Lucio Zambon committed
		return new THREE.CatmullRomCurve3(bpmCurve);
	}
	let bpmInited = false;
Lucio Zambon's avatar
Lucio Zambon committed
	const bpmTimeLen = 3;
Lucio Zambon's avatar
Lucio Zambon committed
	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;
Lucio Zambon's avatar
Lucio Zambon committed
	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]);
Lucio Zambon's avatar
Lucio Zambon committed
			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);
			}
Lucio Zambon's avatar
Lucio Zambon committed
		}
		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>');
	}
Lucio Zambon's avatar
Lucio Zambon committed
	function initShortcut(lattice) {
		const index = [];
Lucio Zambon's avatar
Lucio Zambon committed
		for (let l in lattice.conf.shortcut['3d']) {
			const cmd = "document.location = document.location.pathname + '?"+lattice.conf.shortcut['3d'][l]+"';";
Lucio Zambon's avatar
Lucio Zambon committed
			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');
	}
Lucio Zambon's avatar
Lucio Zambon committed
	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);}
Lucio Zambon's avatar
Lucio Zambon committed
				// console.log('fetch()', latticeFile, i, params[i]);
Lucio Zambon's avatar
Lucio Zambon committed
				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) {
Lucio Zambon's avatar
Lucio Zambon committed
						$('.controller.boolean:last').append('<input id="bpmfreq" type="text" spellcheck="false" aria-labelledby="lil-gui-name-6">');
Lucio Zambon's avatar
Lucio Zambon committed
						params.bpmZoomH = 1;
						gui.add(params, 'bpmZoomH', 0.01, 100);
						params.bpmZoomV = 1;
						gui.add(params, 'bpmZoomV', 0.01, 100);
Lucio Zambon's avatar
Lucio Zambon committed
						params.bpmReference = '2GeV';
Lucio Zambon's avatar
Lucio Zambon committed
						gui.add(params, 'bpmReference');
Lucio Zambon's avatar
Lucio Zambon committed
						if ($('.name:contains("bpmReference")') && $('.name:contains("bpmReference")').parent()) {
Lucio Zambon's avatar
Lucio Zambon committed
							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\")'>");
Lucio Zambon's avatar
Lucio Zambon committed
						}
Lucio Zambon's avatar
Lucio Zambon committed
					}
				}
				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);
Lucio Zambon's avatar
Lucio Zambon committed
		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');}
Lucio Zambon's avatar
Lucio Zambon committed
		for (let i in lattice) {if (i!='conf') initLattice(lattice[i].sections, i);}
Lucio Zambon's avatar
Lucio Zambon committed
		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;}
Lucio Zambon's avatar
Lucio Zambon committed
						}
					}
Lucio Zambon's avatar
Lucio Zambon committed
				}
				console.log('vlvSrc', data, vlvSrc, conf.bstmap, vlvs);
			})
			.catch(error => {console.log("Fetch error", error);});
			setInterval(updateVlv, 1000);
		}
Lucio Zambon's avatar
Lucio Zambon committed
		if (document.location.search.indexOf('search=')>-1) findComponent(document.location.search.split('search=')[1].split('&')[0], false);
Lucio Zambon's avatar
Lucio Zambon committed
		if (lattice.conf && lattice.conf.shortcut["3d"]) initShortcut(lattice);
Lucio Zambon's avatar
Lucio Zambon committed
		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++) {
Lucio Zambon's avatar
Lucio Zambon committed
					if (typeof statSrc[j].split('/')[3] != 'undefined') statSrc[j] = statSrc[j].split('/')[3].replace('PS', '');
Lucio Zambon's avatar
Lucio Zambon committed
					// 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);
Lucio Zambon's avatar
Lucio Zambon committed
			})
Lucio Zambon's avatar
Lucio Zambon committed
			.catch(error => {console.log('stateSrcUrl: '+conf.stateSrcUrl, "Fetch error", error);});
Lucio Zambon's avatar
Lucio Zambon committed
			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+'">&nbsp;</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);
			}
Lucio Zambon's avatar
Lucio Zambon committed
			setInterval(updateStatus, 1000);
		}
Lucio Zambon's avatar
Lucio Zambon committed
		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();
Lucio Zambon's avatar
Lucio Zambon committed
	})
	.catch(error => {console.log("Fetch error", error);});
Lucio Zambon's avatar
Lucio Zambon committed
	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');
	}
Lucio Zambon's avatar
Lucio Zambon committed
	function showStatus(i, stat) {
Lucio Zambon's avatar
Lucio Zambon committed
		status[i].scale.x = 1;
		status[i].scale.y = 1;
		status[i].scale.z = 1;
Lucio Zambon's avatar
Lucio Zambon committed
		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;
		}
Lucio Zambon's avatar
Lucio Zambon committed
		else {status[i].visible = true; status[i].material.color.set(conf.stateLabelColor[stat]);}
Lucio Zambon's avatar
Lucio Zambon committed
		// if (i==40) console.log(i, status[i], stat);
Lucio Zambon's avatar
Lucio Zambon committed
	}
	function clearStatus() {
		for (let i=0; i<status.length; i++) {
Lucio Zambon's avatar
Lucio Zambon committed
			if (status[i].statsrc==ps && statVal[status[i].statindex]!=='ON' && statVal[status[i].statindex]!=='RUNNING') showStatus(i, 0);
Lucio Zambon's avatar
Lucio Zambon committed
		}
	}
Lucio Zambon's avatar
Lucio Zambon committed
	let statVal;
Lucio Zambon's avatar
Lucio Zambon committed
	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;
	}
Lucio Zambon's avatar
Lucio Zambon committed
	function updateStatus() {
Lucio Zambon's avatar
Lucio Zambon committed
		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));
Lucio Zambon's avatar
Lucio Zambon committed
		fetch(conf.stateUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => {
Lucio Zambon's avatar
Lucio Zambon committed
			// console.log('updateStatus()', fel1, fel2, status);
Lucio Zambon's avatar
Lucio Zambon committed
			statVal = data.split(';');
Lucio Zambon's avatar
Lucio Zambon committed
			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]);
				}
			}
Lucio Zambon's avatar
Lucio Zambon committed
			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);
			}
Lucio Zambon's avatar
Lucio Zambon committed
			setTimeout(clearStatus, 600);
Lucio Zambon's avatar
Lucio Zambon committed
			fetchError = 0;
			$('#fetchError').hide();
Lucio Zambon's avatar
Lucio Zambon committed
		})
Lucio Zambon's avatar
Lucio Zambon committed
		.catch(error => {
			console.log("Fetch error", error);
			fetchError++;
			if (fetchError>3) {$('#fetchError').show();}
		});
Lucio Zambon's avatar
Lucio Zambon committed
	}
	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);
Lucio Zambon's avatar
Lucio Zambon committed
		})
		.catch(error => {console.log("Fetch error", error);});
Lucio Zambon's avatar
Lucio Zambon committed
	}
	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});
Lucio Zambon's avatar
Lucio Zambon committed
	const sphereGeometry = new THREE.SphereGeometry(500, 10, 10);
Lucio Zambon's avatar
Lucio Zambon committed
	const rot = {x: $('#rotx').val(), y: $('#roty').val(), z: $('#rotz').val()};
	let gltfscene;
Lucio Zambon's avatar
Lucio Zambon committed

Lucio Zambon's avatar
Lucio Zambon committed
	function pushComponent(facility, i, m, tang, direction, y=Ydefault) {
Lucio Zambon's avatar
Lucio Zambon committed
		let magnet = m>-1? lattice[facility].sections[i].components[m].type: 'dipole';
Lucio Zambon's avatar
Lucio Zambon committed
		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) {
Lucio Zambon's avatar
Lucio Zambon committed
		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', '');
Lucio Zambon's avatar
Lucio Zambon committed
		const gltfscene = new THREE.Object3D();
		gs.scale.set(900, 900, 900);
Lucio Zambon's avatar
Lucio Zambon committed
		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);}
Lucio Zambon's avatar
Lucio Zambon committed
		gltfscene.add(gs);
Lucio Zambon's avatar
Lucio Zambon committed
		console.log('placemagnet', gltfscene, gltfscene.rotation, gltfscene.rotation.x, gltfscene.rotation.z);
Lucio Zambon's avatar
Lucio Zambon committed
		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;
Lucio Zambon's avatar
Lucio Zambon committed
			const myname = m==-1? lattice[facility].sections[i].bending.name: lattice[facility].sections[i].components[m].name;
Lucio Zambon's avatar
Lucio Zambon committed
			// console.log(facility, i, m, myname);
Lucio Zambon's avatar
Lucio Zambon committed
			const id = extractId(myname);
Lucio Zambon's avatar
Lucio Zambon committed
			const d = m==-1? 0: lattice[facility].sections[i].components[m].position;
Lucio Zambon's avatar
Lucio Zambon committed
			const den = Math.sqrt(tang*tang+1) * direction;
Lucio Zambon's avatar
Lucio Zambon committed
			const offset = m>-1 && lattice[facility].sections[i].components[m].offset3d? lattice[facility].sections[i].components[m].offset3d: [0, 0, 0];
Lucio Zambon's avatar
Lucio Zambon committed
			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));
Lucio Zambon's avatar
Lucio Zambon committed
			window.names.push(myname);
			if (m>-1 && lattice[facility].sections[i].components[m].embedded) {
Lucio Zambon's avatar
Lucio Zambon committed
				// 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;
Lucio Zambon's avatar
Lucio Zambon committed
			mycomp.name = myname;
Lucio Zambon's avatar
Lucio Zambon committed
			if (direction>0) mycomp.rotateY(Math.PI);
Lucio Zambon's avatar
Lucio Zambon committed
			compBuffer[magnet][l].name = mycomp.name;
Lucio Zambon's avatar
Lucio Zambon committed
			if (m>-1 && lattice[facility].sections[i].components[m].href) mycomp.href = lattice[facility].sections[i].components[m].href;
Lucio Zambon's avatar
Lucio Zambon committed
			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 ) {
Lucio Zambon's avatar
Lucio Zambon committed
			console.log(magnet);
Lucio Zambon's avatar
Lucio Zambon committed
			console.error( error );
		});
	}
Lucio Zambon's avatar
Lucio Zambon committed
	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;
Lucio Zambon's avatar
Lucio Zambon committed
		// if (magnet=='quadrupole') return;
		// if (glb.indexOf(magnet)>-1) return;
		if (real && typeof conf.real[magnet] !== 'undefined') return;
Lucio Zambon's avatar
Lucio Zambon committed
		if (stl && typeof conf.stl[magnet] !== 'undefined') return;
Lucio Zambon's avatar
Lucio Zambon committed
		if (high && typeof conf.high[magnet] !== 'undefined') return;
Lucio Zambon's avatar
Lucio Zambon committed
		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);
Lucio Zambon's avatar
Lucio Zambon committed
			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]]);}
Lucio Zambon's avatar
Lucio Zambon committed
			}
			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});}
Lucio Zambon's avatar
Lucio Zambon committed
			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++) {
Lucio Zambon's avatar
Lucio Zambon committed
						const sphere = new THREE.Mesh(sphereGeometry,normalMaterial.clone());
Lucio Zambon's avatar
Lucio Zambon committed
						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';
Lucio Zambon's avatar
Lucio Zambon committed
						sphere.facility = facility;
						sphere.visible = false;
						status.push(sphere);
						facilities[facility].add(sphere);
					}
				}
				else {
Lucio Zambon's avatar
Lucio Zambon committed
					const sphere = new THREE.Mesh(sphereGeometry,normalMaterial.clone());
Lucio Zambon's avatar
Lucio Zambon committed
					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));
Lucio Zambon's avatar
Lucio Zambon committed
					sphere.name = mycomp.name+'_status';
Lucio Zambon's avatar
Lucio Zambon committed
					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) {
Lucio Zambon's avatar
Lucio Zambon committed
		// console.log('initLattice()', flattice, facility);
Lucio Zambon's avatar
Lucio Zambon committed
		compData[facility] = [];
Lucio Zambon's avatar
Lucio Zambon committed
		if (lattice[facility] && lattice[facility].bpm) {bpmPoints[facility] = []; bpmSkip[facility] = []; bpmData[facility] = []; }
Lucio Zambon's avatar
Lucio Zambon committed
		console.log('compData', compData, facility);
Lucio Zambon's avatar
Lucio Zambon committed
		facilities[facility] = new THREE.Object3D();
Lucio Zambon's avatar
Lucio Zambon committed
		const wallColor = lattice.conf.wallColor? lattice.conf.wallColor: 'lightgray';
Lucio Zambon's avatar
Lucio Zambon committed
		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')) {
Lucio Zambon's avatar
Lucio Zambon committed
				const chamberMesh = flattice[j].chamber.type=='chamber'? chamber(segmentLen+15): wall(segmentLen+15, wallColor);
Lucio Zambon's avatar
Lucio Zambon committed
				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;
Lucio Zambon's avatar
Lucio Zambon committed
				// if (flattice[j].chamber.type=='wall') console.log(flattice[j].start, yy, Ydefault);
Lucio Zambon's avatar
Lucio Zambon committed
				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';
Lucio Zambon's avatar
Lucio Zambon committed
			const direction = (facility=='servicearea'? 1: 1)*((flattice[j].start.z<flattice[i].start.z /*!= flattice[j].start.x<=flattice[i].start.x*/)? -1: 1);
Lucio Zambon's avatar
Lucio Zambon committed
			if (document.location.search.indexOf('components=hide')==-1 && flattice[j].bending && bendingType && typeof componentCreator[bendingType]!== 'undefined') {
Lucio Zambon's avatar
Lucio Zambon committed
				if (real && typeof conf.real[bendingType] !== 'undefined') {
					pushComponent(facility, i, -1, tang, direction, y);
				}
Lucio Zambon's avatar
Lucio Zambon committed
				else if (stl && typeof conf.stl[bendingType] !== 'undefined') {
Lucio Zambon's avatar
Lucio Zambon committed
					pushComponent(facility, i, -1, tang, direction, y);
				}
Lucio Zambon's avatar
Lucio Zambon committed
				else if (high && typeof conf.high[bendingType] !== 'undefined') {
Lucio Zambon's avatar
Lucio Zambon committed
					pushComponent(facility, i, -1, tang, direction, y);
				}
Lucio Zambon's avatar
Lucio Zambon committed
				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;
Lucio Zambon's avatar
Lucio Zambon committed
					facilities[facility].add(component[dipoleNum]);
Lucio Zambon's avatar
Lucio Zambon committed
					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++;
					}
Lucio Zambon's avatar
Lucio Zambon committed
				}
				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++) {
Lucio Zambon's avatar
Lucio Zambon committed
				// if (facility=='servicearea') console.log(facility, i, m, tang, direction);
Lucio Zambon's avatar
Lucio Zambon committed
				const position = appendComponent(facility, i, m, tang, direction, y);
Lucio Zambon's avatar
Lucio Zambon committed
				// console.log(position, compData, facility);
Lucio Zambon's avatar
Lucio Zambon committed
				// 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]);
Lucio Zambon's avatar
Lucio Zambon committed
				pushComponent(facility, i, m, tang, direction, y);
Lucio Zambon's avatar
Lucio Zambon committed
				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);}
Lucio Zambon's avatar
Lucio Zambon committed
					else bpmPoints[facility].push([position.clone(), Math.atan(tang), direction]);
Lucio Zambon's avatar
Lucio Zambon committed
				}
			}
			envelopeAdd(flattice, i, j, facility, direction);
		}
Lucio Zambon's avatar
Lucio Zambon committed
		// console.log('compBuffer', compBuffer);
Lucio Zambon's avatar
Lucio Zambon committed
		if (real) for (let i in conf.real) {loadreal(i);}
Lucio Zambon's avatar
Lucio Zambon committed
		if (stl) for (let i in conf.stl) {loadreal(i+'_stl');}
Lucio Zambon's avatar
Lucio Zambon committed
		if (high) for (let i in conf.high) {loadreal(i+'_high');}
Lucio Zambon's avatar
Lucio Zambon committed

		// 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);
Lucio Zambon's avatar
Lucio Zambon committed
		// if (params.tour) setTimeout(switchTour, 2000);
Lucio Zambon's avatar
Lucio Zambon committed
	}
	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++) {
Lucio Zambon's avatar
Lucio Zambon committed
				if (vlvs[i].type=='vlv') componentCreator.vlvUpdate(vlvs[i].comp, vlvVal[vlvs[i].vlvindex]=='CLOSED'? 'red': (vlvVal[vlvs[i].vlvindex]=='OPENED'? 'limegreen': 'grey'));
Lucio Zambon's avatar
Lucio Zambon committed
				if (vlvs[i].type=='bst') {
					if (typeof vlvVal[vlvs[i].vlvindex] != 'string') continue;
					const val = vlvVal[vlvs[i].vlvindex].split(',');
Lucio Zambon's avatar
Lucio Zambon committed
					componentCreator.bstUpdate(vlvs[i].comp, val[0]=='true'? 'limegreen': (val[1]=='true'? 'red': 'grey'));
Lucio Zambon's avatar
Lucio Zambon committed
				}
			}
Lucio Zambon's avatar
Lucio Zambon committed
		})
		.catch(error => {console.log("Fetch error", error);});
Lucio Zambon's avatar
Lucio Zambon committed
	}
	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) {
Lucio Zambon's avatar
Lucio Zambon committed
				if (scene.children[facility].children[comp].name==name) {
Lucio Zambon's avatar
Lucio Zambon committed
					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;
Lucio Zambon's avatar
Lucio Zambon committed
					const selectedObject = scene.children[facility].children[comp];
Lucio Zambon's avatar
Lucio Zambon committed
					if (outline) {outlinePass.selectedObjects = [selectedObject]; addSelectedObject(selectedObject);}
					renderer.render(scene, camera);
					x1 = camera.position.x;
					y1 = camera.position.y;
					z1 = camera.position.z;
Lucio Zambon's avatar
Lucio Zambon committed
					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]);
Lucio Zambon's avatar
Lucio Zambon committed
					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;
Lucio Zambon's avatar
Lucio Zambon committed
					rx2 = scene.children[facility].children[comp].position.x;
					ry2 = scene.children[facility].children[comp].position.y;
					rz2 = scene.children[facility].children[comp].position.z;
Lucio Zambon's avatar
Lucio Zambon committed
					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;}});});

Lucio Zambon's avatar
Lucio Zambon committed
	function initVar(name, defaultVal) {
		return document.location.search.indexOf(name)>-1? document.location.search.split(name+'=')[1].split('&')[0]: defaultVal;
	}
Lucio Zambon's avatar
Lucio Zambon committed
	const tourSpeed = 10; // speed [m/s]
Lucio Zambon's avatar
Lucio Zambon committed
	const tourPeriod = initVar('tourPeriod', 20);
Lucio Zambon's avatar
Lucio Zambon committed
	const alphaPerPeriod = 0.008;
Lucio Zambon's avatar
Lucio Zambon committed
	const tourBackets = initVar('tourBackets', 15);
	const tourBacketSize = initVar('tourBacketSize', 0.1);
	const tourBacketsDistance = initVar('tourBacketsDistance', 1500);
	const tourHight = initVar('tourHight', 2000);
Lucio Zambon's avatar
Lucio Zambon committed
	const tourTransverse = initVar('tourTransverse', 1.1);
Lucio Zambon's avatar
Lucio Zambon committed
	const backets = [];
Lucio Zambon's avatar
Lucio Zambon committed
	const showBackets = initVar('showBackets', false);
Lucio Zambon's avatar
Lucio Zambon committed
	function tourCamera(x, y, z, ry) {
Lucio Zambon's avatar
Lucio Zambon committed
		camera.position.set(x*tourTransverse, y, z*tourTransverse);
Lucio Zambon's avatar
Lucio Zambon committed
		camera.rotateY(ry);
		renderer.render(scene, camera);
	}
Lucio Zambon's avatar
Lucio Zambon committed
	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;
Lucio Zambon's avatar
Lucio Zambon committed
			const d = Math.sqrt((ex - bx)*(ex - bx) + (ez - bz)*(ez - bz));
Lucio Zambon's avatar
Lucio Zambon committed
			if (showBackets) for (let backet=0; backet<tourBackets; backet++) {
Lucio Zambon's avatar
Lucio Zambon committed
				placeBacket(backet, facility, section, pos);
			}
Lucio Zambon's avatar
Lucio Zambon committed
			console.log(section, pos, alpha/Math.PI*180);
			const beta = alpha>alphaPerPeriod? alphaPerPeriod: alpha;
			if (alpha>alphaPerPeriod) alpha = alpha - alphaPerPeriod; else alpha = 0;
Lucio Zambon's avatar
Lucio Zambon committed
			tourCamera(bx + pos/d * (ex - bx), tourHight, bz + pos/d * (ez - bz), -beta); // -Math.PI/2
Lucio Zambon's avatar
Lucio Zambon committed
			if (pos+tourSpeed*tourPeriod*9>=d && alpha==0) {
Lucio Zambon's avatar
Lucio Zambon committed
				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);
Lucio Zambon's avatar
Lucio Zambon committed
				console.log(alpha/Math.PI*180);
			}
			if (pos+tourSpeed*tourPeriod>=d) {
				section = ns;
				pos = pos+tourSpeed*tourPeriod - d;
			}
			else pos = pos+tourSpeed*tourPeriod;
Lucio Zambon's avatar
Lucio Zambon committed
			if (params.tour) setTimeout(tour, tourPeriod, facility, section, pos, (alpha<0? 2*Math.PI+alpha: alpha)*0.6);
Lucio Zambon's avatar
Lucio Zambon committed
		}
	}
	function switchTour() {
		console.log(lattice);
Lucio Zambon's avatar
Lucio Zambon committed
		const facility = 'sr';
Lucio Zambon's avatar
Lucio Zambon committed
		if (showBackets && backets.length==0) for (let pi=1; pi<=tourBackets; pi++) {
Lucio Zambon's avatar
Lucio Zambon committed
			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]);
Lucio Zambon's avatar
Lucio Zambon committed
		camera.rotateY(-Math.PI*1);
Lucio Zambon's avatar
Lucio Zambon committed
		if (params.tour) tour(facility, 0, 0, 0);
Lucio Zambon's avatar
Lucio Zambon committed
	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;
	}
Lucio Zambon's avatar
Lucio Zambon committed
	let popupOn = false;
Lucio Zambon's avatar
Lucio Zambon committed

	if (navigator.userAgent.indexOf('Firefox/63')==-1) document.onclick = handleMouseMove;
Lucio Zambon's avatar
Lucio Zambon committed
	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';
Lucio Zambon's avatar
Lucio Zambon committed
		if (document.location.pathname.indexOf('ff63')==-1) document.getElementById('tooltip').style.display = 'none';
Lucio Zambon's avatar
Lucio Zambon committed
		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', ''))+'&param='+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', ''))+'&param='+intersects[i].object.parent.name;