diff --git a/bpm2d.js b/bpm2d.js
index bfa1d9e11f1a63e867fc23653a0a1202721da451..577f1cc861af7c63fa57ea3af4fcc9a6787081ad 100644
--- a/bpm2d.js
+++ b/bpm2d.js
@@ -1,13 +1,100 @@
 // jshint esversion: 6 
 	const bpmData = {};
 	const corr = {};
+	const bpmscalesteps = 10;
+	let bpmFactorHor = document.location.search.indexOf('bpmFactorHor=')>-1? document.location.search.split('bpmFactorHor=')[1].split('&')[0]*1000: 1000;
+	let bpmFactorVer = document.location.search.indexOf('bpmFactorVer=')>-1? document.location.search.split('bpmFactorVer=')[1].split('&')[0]*1000: 1000;
+	let bpmmaxVer = bpmFactorVer*10;
+	let bpmmaxHor = bpmFactorVer*10;
+	/*rulerInit();
+	main.addEventListener('mousemove', function(e) {getRulerPosition(main, e, lattice.sr? 'sr': 'fel1');});
+	if (ruler) {
+		main.style.cursor = 'crosshair';
+	}
+	else $('#rulerg').hide();*/
+	function bpmelapseHor(step, up) {
+		// console.log('elapseHor()',step,scaleHor.$fill.style.width, (bpmscalesteps-step)/bpmscalesteps*90);
+		if (up) {
+			if (step<bpmscalesteps) {const pc = Math.round((bpmscalesteps-step)/bpmscalesteps*90); bpmscaleHor.$fill.style.width=pc+"%";setTimeout(bpmelapseHor, 20, step+1, up); return;}
+			const buf = scaleHor._max; 
+			scaleHor.max(buf*10); 
+			scaleHor.min(buf/10); 
+			scaleHor.step(buf/100);
+		}
+		else {
+			if (step<bpmscalesteps) {const pc = Math.round(step/bpmscalesteps*10); bpmscaleHor.$fill.style.width=pc+"%";setTimeout(bpmelapseHor, 20, step+1, up); return;}
+			const buf = scaleHor._max/10; 
+			scaleHor.max(buf); 
+			scaleHor.min(buf/100);
+			scaleHor.step(buf/1000);
+		}
+		scaleHor.$fill.style.width="10%";
+		scaleHor.$fill.style.borderColor = '#2cc9ff';
+	}
+	function bpmelapseVer(step, up) {
+		if (up) {
+			if (step<bpmscalesteps) {const pc = Math.round((bpmscalesteps-step)/bpmscalesteps*90); bpmscaleVer.$fill.style.width=pc+"%";setTimeout(bpmelapseVer, 20, step+1, up); return;}
+			const buf = scaleVer._max; 
+			scaleVer.max(buf*10); 
+			scaleVer.min(buf/10); 
+			scaleVer.step(buf/100);
+		}
+		else {
+			if (step<bpmscalesteps) {const pc = Math.round(step/bpmscalesteps*10); bpmscaleVer.$fill.style.width=pc+"%";setTimeout(bpmelapseVer, 20, step+1, up); return;}
+			const buf = scaleVer._max/10; 
+			scaleVer.max(buf); 
+			scaleVer.min(buf/100);
+			scaleVer.step(buf/1000);
+		}
+		scaleVer.$fill.style.width="10%";
+		scaleVer.$fill.style.borderColor = '#2cc9ff';
+	}
+	function bpmfinishHor() {
+		// console.log('finishHor()', bpmscaleHor.$fill.style.width);
+		if (bpmscaleHor.$fill.style.width=="100%" || bpmscaleHor.$fill.style.width=="0%") {
+			setTimeout(bpmelapseHor, 200, 1, bpmscaleHor.$fill.style.width=="100%");
+		}
+	}
+	function bpmfinishVer() {
+		if (bpmscaleVer.$fill.style.width=="100%" || bpmscaleVer.$fill.style.width=="0%") {
+			setTimeout(bpmelapseVer, 200, 1, bpmscaleVer.$fill.style.width=="100%");
+		}
+	}
+	function bpmfactorHor(v) {
+		// console.log('factorHor(v)', v, scaleHor.$fill.style.width);
+		bpmscaleHor.$fill.style.borderColor = (bpmscaleHor.$fill.style.width=="100%" || bpmscaleHor.$fill.style.width=="0%")? 'yellow': '#2cc9ff';
+		bpmFactorHor = v*1000;
+		rulerScale('bpm', 1000);
+	}
+	function bpmfactorVer(v) {
+		console.log('factorVer(v)', v, bpmscaleVer.$fill.style.width);
+		bpmscaleVer.$fill.style.borderColor = (bpmscaleVer.$fill.style.width=="100%" || bpmscaleVer.$fill.style.width=="0%")? 'yellow': '#2cc9ff';
+		bpmFactorVer = v*1000;
+		rulerScale('bpm', 1000);
+	}
+	function bpmguiscale(gui, id) {
+		const i = gui.children.length - 1;
+		gui.children[i].domElement.style.display = 'none';
+		gui.children[i].domElement.setAttribute('id', id);
+	}
 	function bpmMenu(lattice, facilities, params) {
 		if (document.location.search.indexOf('old')>-1) {bpmMenuOld(lattice, facilities, params); return;}
 		params.bpm = false;
 		$('.bpmhor').css('display', 'none');
 		$('.bpmver').css('display', 'none');
+			/*menuParams['ruler'] = ruler;
+			gui.add(menuParams, 'ruler').onChange(function() {rulerSwitch();});
+			guiscale(gui, 'ruler');*/
 		params.bpm = document.location.search.indexOf('bpm')>-1 && document.location.search.indexOf('=bpm')==-1;
 		gui.add(params, 'bpm').name('bpm H <img id="h" src="red.svg" style="margin-bottom: -4px;">&nbsp;&nbsp;&nbsp;V <img id="v" src="green.svg" style="margin-bottom: -4px;"> ').onChange(function() {bpmSwitch(bpmData, params);});
+		menuParams['bpmscale H'] = bpmFactorHor/1000;
+		bpmscaleHor = gui.add(menuParams, 'bpmscale H', bpmmaxVer/10000, bpmmaxVer/1000, Math.round(bpmmaxVer/10000)).name('scale H').onChange(function() {bpmfactorHor(menuParams['bpmscale H'], gui);}).onFinishChange(function() {bpmfinishHor(gui);});
+		bpmguiscale(gui, 'bpmscaleh');
+		$('#bpmscaleh').children().eq(0).css('color','red');
+		menuParams['bpmscale V'] = bpmFactorHor/1000;
+		bpmscaleVer = gui.add(menuParams, 'bpmscale V', bpmmaxHor/1000, bpmmaxHor/100, Math.round(bpmmaxHor/10000)).name('scale V').onChange(function() {bpmfactorVer(menuParams['bpmscale V'], gui);}).onFinishChange(function() {bpmfinishVer(gui);});
+		bpmguiscale(gui, 'bpmscalev');
+		$('#bpmscalev').children().eq(0).css('color','green');
 		for (let f in facilities) {
 			const b = facilities[f];
 			if (b=='' || typeof bpmData[b] == 'undefined' || typeof lattice[b].bpm == 'undefined' || typeof lattice[b].bpm.orbitconf == 'undefined') continue;
@@ -57,8 +144,8 @@
 						  Math.round(bpmData[facility].pos[i][1] + f*1.05*Math.cos(beta))
 					);
 				}
-				appendSvg("path", {id:facility+"_bpmhor", class: "bpmhor "+facility, visibility:"hidden", name:"bpmhor", fill: "none", "stroke-width":"100", "stroke":"red", "stroke-opacity":"0.6", d: dhor.join(' ')+(lattice[facility].sections[0].chamber?' Z':'')});
-				appendSvg("path", {id:facility+"_bpmver", class: "bpmver "+facility, visibility:"hidden", name:"bpmver", fill: "none", "stroke-width":"100", "stroke":"green", "stroke-opacity":"0.6", d: dver.join(' ')+(lattice[facility].sections[0].chamber?' Z':'')});
+				appendSvg("path", {id:facility+"_bpmhor", class: "bpmhor trajectory "+facility, visibility:"hidden", name:"bpmhor", fill: "none", "stroke-width":"100", "stroke":"red", "stroke-opacity":"0.6", d: dhor.join(' ')+(lattice[facility].sections[0].chamber?' Z':'')});
+				appendSvg("path", {id:facility+"_bpmver", class: "bpmver trajectory "+facility, visibility:"hidden", name:"bpmver", fill: "none", "stroke-width":"100", "stroke":"green", "stroke-opacity":"0.6", d: dver.join(' ')+(lattice[facility].sections[0].chamber?' Z':'')});
 				console.log("path", {id:facility+"_bpmhor", name:"bpmhor", d: dver.join(' ')+' Z'});
 			}
 		}
@@ -70,16 +157,22 @@
 		}
 		bpmData.reader = false;
 		if (params.bpm=='') {
+			$('#bpmscaleh').hide();
+			$('#bpmscalev').hide();
+			$('#ruler').hide();
 			for (let facility in bpmData) {
 				if (typeof $('#'+facility+"_bpmhor").attr('visibility') != 'undefined') {
 					$('#'+facility+"_bpmhor").attr('visibility', "hidden");
 					$('#'+facility+"_bpmver").attr('visibility', "hidden");
 				}
 			}
-			$('#application').hide();
+			// $('#application').hide();
 			$('#applicationFrame').removeAttr("src");
 		} 
 		else {
+			$('#bpmscaleh').show();
+			$('#bpmscalev').show();
+			$('#ruler').show();
 			const bpms = [];
 			for (let i in bpmData) if (i!="reader") bpms.push(i);
 			console.log('bpms', bpms, bpmData);
@@ -94,12 +187,13 @@
 				bpmRead(bpmData, params);
 			}
 			bpmData.reader = setInterval(bpmRead, 200, bpmData, params);
-			$('#application').show();	
+			// $('#application').show();	
 			$('.bpmhor').css('display', 'block');
 			$('.bpmver').css('display', 'block');
 			$('#applicationFrame').attr("src", "/spa/index.html?s=bpm&src="+params.bpm+(document.location.search.indexOf('demo')>-1? '&demo': ''));
 			$("#applicationFrame").height(window.innerHeight); 
 		}		
+		rulerScale('bpm', 1000);
 	}
 	function bpmRender(facility, val) {
 		const dhor = [];
@@ -112,12 +206,12 @@
 			const beta = Math.PI*bpmData[facility].dir[i]/180;
 			// console.log('i', i, 'pos',pos, 'dir', bpmData[facility].dir[i], 'facility', facility, beta, Math.cos(beta), Math.sin(beta));
 			dhor.push((i==0?'M':'L')+ 
-				Math.round(pos[0] - val.Hor[i]*bpmFactor*Math.sin(beta)) + ' ' + 
-				Math.round(pos[1] + val.Hor[i]*bpmFactor*Math.cos(beta))
+				Math.round(pos[0] - val.Hor[i]*bpmFactorHor*Math.sin(beta)) + ' ' + 
+				Math.round(pos[1] + val.Hor[i]*bpmFactorHor*Math.cos(beta))
 			);
 			dver.push((i==0?'M':'L')+ 
-				Math.round(pos[0] - val.Ver[i]*bpmFactor*Math.sin(beta)) + ' ' + 
-				Math.round(pos[1] + val.Ver[i]*bpmFactor*Math.cos(beta))
+				Math.round(pos[0] - val.Ver[i]*bpmFactorVer*Math.sin(beta)) + ' ' + 
+				Math.round(pos[1] + val.Ver[i]*bpmFactorVer*Math.cos(beta))
 			);
 		}
 		$('#'+facility+"_bpmhor").attr('d', dhor.join(' ')+(lattice[facility].sections[0].chamber?' Z':''));