From fdc544200722157bfbe5b37aea5be16b08e08a6f Mon Sep 17 00:00:00 2001
From: Lucio Zambon <lucio.zambon@elettra.eu>
Date: Mon, 19 Aug 2024 01:43:51 +0000
Subject: [PATCH] Update panther2d.js

---
 panther2d.js | 209 ++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 140 insertions(+), 69 deletions(-)

diff --git a/panther2d.js b/panther2d.js
index b1fb6a2..18f5aae 100644
--- a/panther2d.js
+++ b/panther2d.js
@@ -29,19 +29,20 @@
 	for (let i=0; i<pa.length; i++) {const p = pa[i].split('='); parameters[p[0]] = p[1];}
 	const machineCaseSensitive = document.location.search.indexOf('machine=')>-1? document.location.search.split('machine=')[1].split('&')[0]: 'elettra';
 	const machine = machineCaseSensitive.toLowerCase();
-	let rulerStart = false;
+	let measurementStart = false;
 	let cx, cy;
-	const ruler = document.location.search.indexOf('ruler=')>-1? document.location.search.split('ruler=')[1].split('&')[0].split(','): document.location.search.indexOf('ruler')>-1;
-	let rulerEvent = 'contextmenu';
-	if (typeof ruler == "object") {
-		const index = ruler.indexOf('left'); 
+	let measurementType = document.location.search.indexOf('measurement=')>-1? document.location.search.split('measurement=')[1].split('&')[0].split(','): document.location.search.indexOf('measurement')>-1;
+	let measurementEvent = 'contextmenu';
+	if (typeof measurementType == "object") {
+		const index = measurementType.indexOf('left'); 
 		if (index>-1) {
-			rulerEvent = 'mousedown';
-			ruler.splice(index, 1);
+			measurementEvent = 'mousedown';
+			measurementType.splice(index, 1);
 		}
-		if (ruler.length==0) $('#bm').hide();
+		if (measurementType.length==0) $('#bm').hide();
 	}
 	else $('#bm').hide();
+	let measurement = typeof measurementType == "object" || measurementType;
 
 	function setTick(x, y, cx, cy, dist, distPixel, n, pow10=1000) {
 		const tx1 = cx + (x - cx) / dist * pow10 * n;
@@ -89,14 +90,14 @@
 		const y = event.clientY - rect.top;
 		const distPixel = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2));
 		const dist = distPixel / panZoomPanther.getSizes().realZoom;
-		if (rulerStart) $('#stright').val(Math.round(dist)/1000);
+		if (measurementStart) $('#stright').val(Math.round(dist)/1000);
 		// const alpha = (Math.atan2(y-cy, x-cx) * 180) / Math.PI;
 		$('#pos').val(Math.round((x - panZoomPanther.getPan().x)/panZoomPanther.getSizes().realZoom)/1000+' '+(-Math.round((y - panZoomPanther.getPan().y)/panZoomPanther.getSizes().realZoom)/1000));
-		if (rulerStart) {
-			// stright ruler
+		if (measurementStart) {
+			// stright measurement
 			const pow10 = Math.pow(10, Math.round(Math.log10(dist))-1);
-			$('#rulerLine').attr('x2', x); 
-			$('#rulerLine').attr('y2', y);
+			$('#measurementLine').attr('x2', x); 
+			$('#measurementLine').attr('y2', y);
 			for (let i=1; i<32; i++) {
 				if (i<dist/pow10 && distPixel>50) setTick(x, y, cx, cy, dist, distPixel, i, pow10); else setTick(x, y, x, y, dist, distPixel, i, pow10);
 			}
@@ -105,109 +106,162 @@
 			let dindex = -1;
 			let mincDistance = Infinity;
 			let dcindex = -1;
-			// beam pattern ruler
-			if (typeof ruler == "object" && typeof lattice[ruler[0]] == "object") {
+			// beam pattern measurement
+			if (typeof measurementType == "object" && typeof lattice[measurementType[0]] == "object") {
 				let d = 0;
 				const x1 = (x - panZoomPanther.getPan().x) / panZoomPanther.getSizes().realZoom;
 				const y1 = (y - panZoomPanther.getPan().y) / panZoomPanther.getSizes().realZoom;
 				const xc1 = (cx - panZoomPanther.getPan().x) / panZoomPanther.getSizes().realZoom;
 				const yc1 = (cy - panZoomPanther.getPan().y) / panZoomPanther.getSizes().realZoom;
-				for (let i=0; i < lattice[ruler[0]].sections.length; i++) {
-					j = (i+1) % lattice[ruler[0]].sections.length;
-					vertex[i] = lattice[ruler[0]].sections[i].start;
+				for (let i=0; i < lattice[measurementType[0]].sections.length; i++) {
+					j = (i+1) % lattice[measurementType[0]].sections.length;
+					vertex[i] = lattice[measurementType[0]].sections[i].start;
 					// d = Math.sqrt(Math.pow(vertex[i].x - x, 2) + Math.pow(vertex[i].z - y, 2));
-					d = pDistance(x1, y1, vertex[i].x, vertex[i].z, lattice[ruler[0]].sections[j].start.x, lattice[ruler[0]].sections[j].start.z);
+					d = pDistance(x1, y1, vertex[i].x, vertex[i].z, lattice[measurementType[0]].sections[j].start.x, lattice[measurementType[0]].sections[j].start.z);
 					if (minDistance > d.dist) { minDistance = d.dist; dindex = i; p = d;}
-					d = pDistance(xc1, yc1, vertex[i].x, vertex[i].z, lattice[ruler[0]].sections[j].start.x, lattice[ruler[0]].sections[j].start.z);
+					d = pDistance(xc1, yc1, vertex[i].x, vertex[i].z, lattice[measurementType[0]].sections[j].start.x, lattice[measurementType[0]].sections[j].start.z);
 					if (mincDistance > d.dist) { mincDistance = d.dist; dcindex = i; pc = d;}
 				}
 				let pathd = 'M '+(pc.x*panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x)+' '+(pc.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y)+' ';				
 				k = dcindex;
-				j = (k+1) % lattice[ruler[0]].sections.length;
-				let dd = Math.sqrt(Math.pow(pc.x-lattice[ruler[0]].sections[j].start.x, 2) + Math.pow(pc.y-lattice[ruler[0]].sections[j].start.z, 2));
+				j = (k+1) % lattice[measurementType[0]].sections.length;
+				let dd = Math.sqrt(Math.pow(pc.x-lattice[measurementType[0]].sections[j].start.x, 2) + Math.pow(pc.y-lattice[measurementType[0]].sections[j].start.z, 2));
 				if (dcindex == dindex) dd = Math.sqrt(Math.pow(pc.x-p.x, 2) + Math.pow(pc.y-p.y, 2));
 				else {
 					while (k != dindex) {
-						j = (k+1) % lattice[ruler[0]].sections.length;
-						if (k != dcindex) dd = dd + Math.sqrt(Math.pow(lattice[ruler[0]].sections[k].start.x-lattice[ruler[0]].sections[j].start.x, 2) + Math.pow(lattice[ruler[0]].sections[k].start.z-lattice[ruler[0]].sections[j].start.z, 2));
-						pathd = pathd + 'L '+(lattice[ruler[0]].sections[j].start.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x)+' '+(lattice[ruler[0]].sections[j].start.z * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y)+' ';
+						j = (k+1) % lattice[measurementType[0]].sections.length;
+						if (k != dcindex) dd = dd + Math.sqrt(Math.pow(lattice[measurementType[0]].sections[k].start.x-lattice[measurementType[0]].sections[j].start.x, 2) + Math.pow(lattice[measurementType[0]].sections[k].start.z-lattice[measurementType[0]].sections[j].start.z, 2));
+						pathd = pathd + 'L '+(lattice[measurementType[0]].sections[j].start.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x)+' '+(lattice[measurementType[0]].sections[j].start.z * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y)+' ';
 						k = j;
 					}
-					dd = dd + Math.sqrt(Math.pow(p.x-lattice[ruler[0]].sections[dindex].start.x, 2) + Math.pow(p.y-lattice[ruler[0]].sections[dindex].start.z, 2));
+					dd = dd + Math.sqrt(Math.pow(p.x-lattice[measurementType[0]].sections[dindex].start.x, 2) + Math.pow(p.y-lattice[measurementType[0]].sections[dindex].start.z, 2));
 				}
 				$('#bm').val(Math.round(dd)/1000);
 				pathd = pathd + 'L '+(p.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x)+' '+(p.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y);
 				//<path d="M 10 10 H 90 V 90 H 10 L 10 10"/>
 				console.log('minDistance', mincDistance, dcindex, pc, minDistance, dindex, p, dd, 'pathd', pathd);
 				$('#beam').attr('d', pathd);
-				$('#rulerSide1').attr('x2', pc.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x);
-				$('#rulerSide1').attr('y2', pc.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y);
-				$('#rulerSide2').attr('x1', x);
-				$('#rulerSide2').attr('y1', y);
-				$('#rulerSide2').attr('x2', p.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x);
-				$('#rulerSide2').attr('y2', p.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y);
+				$('#measurementSide1').attr('x2', pc.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x);
+				$('#measurementSide1').attr('y2', pc.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y);
+				$('#measurementSide2').attr('x1', x);
+				$('#measurementSide2').attr('y1', y);
+				$('#measurementSide2').attr('x2', p.x * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().x);
+				$('#measurementSide2').attr('y2', p.y * panZoomPanther.getSizes().realZoom + panZoomPanther.getPan().y);
 			}
 		}
 	}
-	function clearRuler() {
-		$('#rulerLine').attr('x1', cx);
-		$('#rulerLine').attr('y1', cy);
-		$('#rulerLine').attr('x2', cx);
-		$('#rulerLine').attr('y2', cy);
-		$('#rulerSide1').attr('x1', cx);
-		$('#rulerSide1').attr('y1', cy);
-		$('#rulerSide1').attr('x2', cx);
-		$('#rulerSide1').attr('y2', cy);
-		$('#rulerSide2').attr('x1', cx);
-		$('#rulerSide2').attr('y1', cy);
-		$('#rulerSide2').attr('x2', cx);
-		$('#rulerSide2').attr('y2', cy);
+	function clearmeasurement() {
+		$('#measurementLine').attr('x1', cx);
+		$('#measurementLine').attr('y1', cy);
+		$('#measurementLine').attr('x2', cx);
+		$('#measurementLine').attr('y2', cy);
+		$('#measurementSide1').attr('x1', cx);
+		$('#measurementSide1').attr('y1', cy);
+		$('#measurementSide1').attr('x2', cx);
+		$('#measurementSide1').attr('y2', cy);
+		$('#measurementSide2').attr('x1', cx);
+		$('#measurementSide2').attr('y1', cy);
+		$('#measurementSide2').attr('x2', cx);
+		$('#measurementSide2').attr('y2', cy);
 		$('#stright').val('');
 		$('#bm').val('');
 		$('#beam').attr('d', '');
 		for (let i=1; i<32; i++) {setTick(cx, cy, cx, cy, 1, 1, i);}
 	}
 	function initCursorPosition(main, event) {
-		if (rulerStart) { rulerStart = false; return;}
+		if (measurementStart) { measurementStart = false; return;}
 		const rect = main.getBoundingClientRect();
 		cx = event.clientX - rect.left;
 		cy = event.clientY - rect.top;
 		console.log(Math.round(cx) + "," + Math.round(cy) + ",");
-		if (!rulerStart) {
-			// appendSvg("circle", {id: 'rulerStart', cx: cx, cy: cy, r:8, style:"display: block", fill:"none", stroke:"red","stroke-width":2});
-			if ($('#rulerLine').length==0) {
-				appendSvg("line", {id: 'rulerLine', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"yellow","stroke-width":2});
+		if (!measurementStart) {
+			// appendSvg("circle", {id: 'measurementStart', cx: cx, cy: cy, r:8, style:"display: block", fill:"none", stroke:"red","stroke-width":2});
+			if ($('#measurementLine').length==0) {
+				appendSvg("line", {id: 'measurementLine', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"yellow","stroke-width":2});
 				appendSvg("path", {d: '', fill: "none", stroke: "limegreen", "stroke-width": 3, id:"beam"});
-				appendSvg("line", {id: 'rulerSide1', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"pink","stroke-width":2});
-				appendSvg("line", {id: 'rulerSide2', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"pink","stroke-width":2});
+				appendSvg("line", {id: 'measurementSide1', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"pink","stroke-width":2});
+				appendSvg("line", {id: 'measurementSide2', x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"pink","stroke-width":2});
 				for (let i=1; i<32; i++) {
 					appendSvg("line", {id: 'tick'+i, x1: cx, y1: cy, x2: cx, y2: cy, style:"display: block", fill:"none", stroke:"yellow","stroke-width":2});
 					appendSvg("text", {id: 'tickLabel'+i, x: cx, y: cy, fill:"white", stroke:"#eeeeee","stroke-width":1, "font-family":"Arial", "font-size":12, "font-weight":"bold", "text-anchor": "start"});
 				}
 			}
 			else {
-				clearRuler();
+				clearmeasurement();
 			}
-			rulerStart = true;
+			measurementStart = true;
 		}
 		else {
-			// $('#rulerStart').attr('cx', cx);	$('#rulerStart').attr('cy', cy);
-			$('#rulerLine').attr('x1', cx);
-			$('#rulerLine').attr('y1', cy);
+			// $('#measurementStart').attr('cx', cx);	$('#measurementStart').attr('cy', cy);
+			$('#measurementLine').attr('x1', cx);
+			$('#measurementLine').attr('y1', cy);
 		}
 		main.addEventListener('mousemove', function(e) {
 			getCursorPosition(main, e);
 		});
 	}
-	if (document.location.search.indexOf('ruler')>-1) {
-		$('#ruler').show();
+	if (document.location.search.indexOf('measurement')>-1) {
+		measurementEnable();
+	}
+	function measurementToggle(enable) {
+		for (let i=1; i<100; i++) {
+			if ($('#lil-gui-name-'+i).html()=='measurement') {
+				if (enable) {
+					$('#lil-gui-name-'+(i+1)).parent().show();
+					$('#lil-gui-name-'+(i+2)).parent().show();
+				}
+				else {
+					$('#lil-gui-name-'+(i+1)).parent().hide();
+					$('#lil-gui-name-'+(i+2)).parent().hide();
+				}
+				break;
+			}
+		}
+	}
+	function measurementDisable() {
+		clearmeasurement();
+		main.removeEventListener(measurementEvent, measurementListener, false);
+		main.style.cursor = 'default';
+		measurementToggle(false);
+	}
+	function measurementListener(e) {
+		e.preventDefault();
+		initCursorPosition(main, e);
+		return false;
+	}
+	function measurementEnable() {
+		$('#measurement').show();
 		const main = document.getElementById('main');
 		main.style.cursor = 'crosshair';
-		main.addEventListener(rulerEvent, function(e) {
-			e.preventDefault();
-			initCursorPosition(main, e);
-			return false;
-		}, false);
+		main.addEventListener(measurementEvent, measurementListener, false);
+		measurementToggle(true);
+	}
+	function measurementFacility(msg) {
+		console.log('measurementFacility()', msg);
+		if (msg=='left') {
+			if (measurementEvent != 'mousedown') {
+				measurementDisable();
+				measurementEvent = 'mousedown';
+				measurementEnable();
+			}
+		}
+		else if (msg=='right') {
+			if (measurementEvent == 'mousedown') {
+				measurementDisable();
+				measurementEvent = 'contextmenu';
+				measurementEnable();
+			}
+		}
+		else if (msg=='') {
+			measurementDisable();
+			measurementType = true;
+			measurementEnable();
+		}
+		else {
+			measurementDisable();
+			measurementType = [msg];
+			measurementEnable();
+		}
 	}
 
 	//https://puma-01.elettra.eu/rchan.php?json&valueOnly&src=srv-tango-srf-01.fcs.elettra.trieste.it:20000/f/access_control/safety/Undulator_access_state -> 5 FEL1, 6 FEL2
@@ -281,6 +335,8 @@
 	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');});
+	params.measurement = document.location.search.indexOf('measurement')>-1;
+	gui.add(params, 'measurement').onChange(function() {toggleParam('measurement');});
 	const sstring = $('.controller.string').children().eq(1).children().eq(0);
 	sstring.attr('id', 'sname');
 	sstring.attr('name', 'sname');
@@ -333,6 +389,7 @@
 	// $(function() {$(".sname").autocomplete({source: names, close: function(event, ui) {findComponent($('#sname').val()); }, select: function(event, ui) {findComponent(ui.item.value); return false;}});});
 	$(function() {$(".sname").autocomplete({source: names, select: function(event, ui) {findComponent(ui.item.value); return false;}});});
 	function toggleParam(name) {
+		if (name=='measurement') { measurement = !measurement; measurementType = measurement; if (measurement) measurementEnable(); else  measurementDisable(); return;}
 		if (name=='backgroundColor') {$('body').css('backgroundColor', params.backgroundColor); return;}
 		const urlparam = {};
 		let search = document.location.search.replace('?', '').split('&');
@@ -392,14 +449,20 @@
 		fetch(latticeFile).then((response) => {return response.json();}).then((flattice) => {
 			lattice = flattice;
 			if (Object.keys(lattice).length>0) {
+				gui.add(params, 'measurement button', {Left: 'left', Right: 'right'}).onChange(measurementFacility);
+				const measurementDevice = {'': ''};
 				for (let i in lattice) {if (i!='conf') facilities.push(i);}
 				for (let i in lattice) {
 					if (i == 'conf') continue;
+					params[i] = false;
+					if (i != 'servicearea' && i != 'bl') measurementDevice[i] = i;
 					// logic XOR https://stackoverflow.com/questions/2335979/is-there-anyway-to-implement-xor-in-javascript
 					if ((document.location.search.indexOf('servicearea')==-1) != (i=='servicearea')) initLattice(lattice[i].sections, i); else {
 						if (document.location.search.indexOf('+servicearea')>-1) initLattice(lattice[i].sections, i); else initSearch(lattice[i].sections, i);
 					}
 				}
+				gui.add(params, 'measurement device', measurementDevice).onChange(measurementFacility);
+				if (document.location.search.indexOf('measurement')==-1) measurementToggle(false);
 				bpmInit(facilities); 
 				if (document.location.search.indexOf('servicearea')>-1) initSearch(lattice.servicearea.sections, 'servicearea');
 				if (typeof blm != 'undefined') blmMenu(lattice, facilities, params);
@@ -873,6 +936,14 @@
 		}
 	};
 	function myPanZoomDelayed(event) {
+		const urlPan = (document.location.search.indexOf('pan=')==-1)? [0, 0]: document.location.search.split('pan=')[1].split('&')[0].split(',');
+		const urlZoom = (document.location.search.indexOf('zoom=')==-1)? 1: document.location.search.split('zoom=')[1].split('&')[0];
+		if (Math.abs(panZoomPanther.getPan().x - urlPan[0]) + Math.abs(panZoomPanther.getPan().y - urlPan[1]) > 1) {
+			setUrl('pan', Math.round(panZoomPanther.getPan().x*100)/100+','+Math.round(panZoomPanther.getPan().y*100)/100);
+		}
+		if (Math.abs(panZoomPanther.getZoom() - urlZoom) > 0.1) {
+			setUrl('zoom', Math.round(panZoomPanther.getZoom()*100)/100);
+		}
 		if (event) event.preventDefault();
 		visibleX = 0 - point.x/zoom;
 		visibleWidth = document.getElementById('panther').clientWidth/zoom;
@@ -880,7 +951,7 @@
 		myPanZoomTimer = null;
 		oldZoom = zoom;
 	}
-	let firstUrl = true;
+	let urlCount = -1;
 	function setUrl(name, value) {
 		let t = + new Date();
 		if (t - historytime < 100) return;
@@ -890,9 +961,9 @@
 		for (let i in parameters) {pp.push(i+'='+parameters[i]);}
 		const url = document.location.origin+document.location.pathname+'?'+pp.join('&').replaceAll('=undefined', '').replace('search=','s=');
 		// console.log('setUrl()',name, value, url);
-		if (firstUrl) window.history.pushState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url);
-		else window.history.replaceState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url);
-		firstUrl = false;
+		if (urlCount==1) window.history.pushState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url);
+		else if (urlCount>1) window.history.replaceState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url);
+		urlCount++;
 	}
 	function myPan(oldPan, newPan) {
 		setUrl('pan', Math.round(newPan.x*100)/100+','+Math.round(newPan.y*100)/100);
@@ -902,7 +973,7 @@
 		// mylog('myPan', p,q);
 	}
 	function myZoom(oldScale, newScale) {
-		clearRuler();
+		clearmeasurement();
 		setUrl('zoom', Math.round(newScale*100)/100);
 		zoom = newScale;
 		if (newScale>2) $('.label').show(); else $('.label').hide();
-- 
GitLab