All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jadex.micro.mandelbrot.webui.mandelbrot.js Maven / Gradle / Ivy

The newest version!
//export class MandelbrotElement extends LitElement 
class MandelbrotElement extends HTMLElement 
{
	static getHost(node)
	{
		let tmp = node; 
		while(tmp!=null && tmp.parentNode!=null) 
			tmp = tmp.parentNode; 
		tmp=tmp.host; 
		//console.log(tmp);
		return tmp;
	}
	
	constructor()
	{
		super();
		this.attachShadow({ mode: 'open'});
     	this.visible = true;
     	window['getHost'] = MandelbrotElement.getHost;
 
     	console.log("init of mandelbrot elem");
     	
     	//let self = this;
     	/*this.app = App.getInstance();
     	this.listener = (type, value) =>
		{
			self.appChanged(type, value);
		};*/	
		// this.listener = this.appChanged; // 'this' is wrong in that case
		/*this.loadStyle("style.css").then(()=>
		{
			self.update();
		})
		.catch(err =>
		{
			self.setError(err);
		});*/
     	
     	this.style = this.addStyle(this.getCss());
		
		//this.update();
	}
	
	update()
	{
		this.shadowRoot.innerHTML = this.getHtml();
		this.initListener();
		this.paint();
	}
	
	// todo: why does caching (above) not work :-(
	loadStyle(url)
	{
		//console.log("###########LOADING STY " + url);
		var self = this;
		var ret = null;
		
		ret = new Promise(function(resolve, reject) 
		{
			axios.get(url).then(function(resp)
			{
				//console.log("loaded version: "+url+" "+self.shadowRoot.adoptedStyleSheets.length);
				//console.log(resp.data);
				var css = resp.data;
				resolve(self.addStyle(css));
				BaseElement.loaded[url] = css;
			})
			.catch(function(err)
			{
				reject(err);
			});
		});
		
		return ret;
	}
	
	addStyle(css)
	{
		var sheet = new CSSStyleSheet();
		sheet.replaceSync(css);
		this.shadowRoot.adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets.concat(sheet);
		return sheet;
	}
	
	connectedCallback() 
	{
		console.log('connected');
		
		let self = this;
		this.displayid = "webgui"+jadex.generateUUID();
		this.colors = null; // the color scheme
		this.data = null; // the area data to draw
		this.progressdata = null; // the progress infos
		this.calculating = false;
		this.startdrag = null;
		this.enddrag  = null;
		this.point = null; // The current selection start point (if any).
		this.range = null;
		
		this.setColorScheme([this.createColor(50, 100, 0), this.createColor(255, 0, 0)], true);
					
		// shadow dom not available here :-(	

		// must not use args_0 as parameter name as this will be made to args list
		this.terminate = jadex.getIntermediate('mandelbrotdisplay/subscribeToDisplayUpdates?a='+this.displayid+'&returntype=jadex.commons.future.ISubscriptionIntermediateFuture',
		function(response)
		{
			//console.log("subscribe received: "+response.data);
			self.handleDisplayUpdate(response);
		},
		function(err)
		{
			console.log("display subscribe err: "+err);
		},
		null,
		function() // init handler
		{
			console.log("display subscribed");
			
			self.getAlgorithms().then(algos =>
			{
				self.algorithms = algos.map(a => 
				{
					let parts = a.value.split(".");
					let name = parts[parts.length-1];
					let suffix = "Algorithm";
					if(name.endsWith(suffix)) 
    					name = name.slice(0, -suffix.length);
					return {classname: a.value, name: name};
				});
				//console.log("received algos: "+self.algorithms);
				self.calcArea();
				self.update();
			})
			.catch(ex =>
			{
				console.log(ex);
			});
		});
	}
	
	disconnectedCallback()
	{
		console.log('disconnected');
		
		if(this.terminate)
			jadex.terminateCall(this.terminate);
	}
	
	initListener() 
	{
		let self = this;
		
		this.addMouseListener(this.shadowRoot.getElementById("canvas"));
		
		this.makeElementDragable(this.shadowRoot.getElementById("settings"));
		this.makeElementDragable(this.shadowRoot.getElementById("image"), true);
		
		// turn off right click popup menu
		this.shadowRoot.getElementById("canvas").oncontextmenu = e => 
		{
    		e.preventDefault();
		};
		
		// get informed on resize events of the canvas
		let ro = new ResizeObserver(entries => 
		{
			for(let entry of entries) 
			{
				//self.checkAdaptSizeToCanvas();
				if(this.isTrackSize())
					self.adaptSizeToCanvas(entry, self);
					
				let adaptresize = this.shadowRoot.getElementById("adaptresize").checked;
				if(adaptresize)
				{
					let canvas = this.shadowRoot.getElementById("canvas");
					canvas.width = canvas.offsetWidth;
					canvas.height = canvas.offsetHeight;
					
					//console.log("resize update");
					self.paint();
					//self.update();
				}				
  			}
		});		
		ro.observe(this.shadowRoot.getElementById("canvas"));
		
		//this.resetSettings(e);
  	}
	
	handleDisplayUpdate(response)
	{
		//console.log("recceived display update: "+response.data);
		let self = this;
		
		let data = response.data;
		//System.out.println("rec: "+result.getClass());
		
		if(data.algorithmClass!=null) // result instanceof AreaData
		{
			//self.calculating = true;
			self.initResults(data);
			self.setSettings(data);
			
			let adaptsize = this.shadowRoot.getElementById("adaptsize").checked;
			if(adaptsize)
			{
				let results = this.data.data;
				let swidth = results.length;
				let sheight = results[0].length;
				let container = this.shadowRoot.getElementById("image");
				container.style.width = swidth+"px";
				container.style.height = sheight+"px";
				console.log("adapted container size: "+container.style.width+" "+container.style.height);
	  		}
		}
		else if(data.data!=null) // result instanceof PartDataChunk
		{
			//console.log("received data");
			
			//self.calculating = true;
			self.addDataChunk(data);
		}
		
		if(data.progress!=null) // result instanceof ProgressData
		{
			//console.log("received progress: "+data.progress);
			
			let prog = {};
			prog.name = data.worker;//.name;
			prog.area = data.area;
			prog.progress = data.progress;
			prog.imageWidth = data.imageWidth;
			prog.imageHeight = data.imageHeight; 
			prog.finished = data.progress==100;
			self.addProgress(prog);
		}	
	}
	
	paint()
	{
		if(this.data==null || this.data.data==null || this.data.image==null)
			return;
			
		let results = this.data.data;
		let oimage = this.data.image;
		let image = new Uint8ClampedArray(oimage); // clone the image data
		//let adaptsize = this.shadowRoot.getElementById("adaptsize").checked;
		//let adaptresize = this.shadowRoot.getElementById("adaptresize").checked;
		let fill = this.shadowRoot.getElementById("fill").checked;
		let container = this.shadowRoot.getElementById("image");
		let canvas = this.shadowRoot.getElementById("canvas");
		let ctx = canvas.getContext("2d");
		let sx = 0;
		let sy = 0;
		let swidth = results.length;
		let sheight = results[0].length;
		let width = canvas.width;
		let height = canvas.height;    
		// screen range, coordinates on canvas
		let srange = this.range!=null? this.convertRangeToScreenValues(this.range): null; 
		
		/*if(canvas.width 
		{
			ctx.drawImage(imgbitmap, sx, sy, swidth, sheight, tx, ty, twidth, theight);
		})
		.catch(err =>
		{
			console.log(err);
		});*/
		
		let canvas2 = null;
		let ctx2 = null;
		// Draw progress boxes.
		if(this.progressdata!=null && Object.keys(this.progressdata).length>0)
		{
			//canvas2 = this.createCanvas(swidth, sheight);
			let w = canvas.offsetWidth;
			let h = canvas.offsetHeight;
			
			canvas2 = this.createCanvas(w, h);
			ctx2 = canvas2.getContext('2d');
			
			for(let key of Object.keys(this.progressdata)) 
			{
				let progress = this.progressdata[key];
				
				//console.log("progress is: "+progress);
					
				let xf = fill? w/progress.imageWidth: 1;
				let yf = fill? h/progress.imageHeight: 1;
				let corx = Math.trunc(progress.area.x*xf);
				let cory = Math.trunc(progress.area.y*yf);
				let corw = Math.trunc(progress.area.w*xf);
				let corh = Math.trunc(progress.area.h*yf);
				let mystartx = fill? corx: corx+startx;
				let mystarty = fill? cory: cory+starty;
					
				if(!progress.finished)
				{
					//ctx2.fillStyle = this.createColor(20, 20, 150, 160); //160
					ctx2.fillStyle = "rgba(20, 20, 150, 0.3)";
					ctx2.fillRect(mystartx+1, mystarty+1, corw-1, corh-1);
				}
				ctx2.strokeStyle = "white";
				ctx2.strokeRect(mystartx, mystarty, corw, corh);
				
				// Print worker name
				let name = progress.name? progress.name: "n/a";
				let provider = "";
				if(name)
				{
					let index =	name.indexOf('@');
					if(index!=-1)
					{
						provider = name.substring(index+1);
						name = name.substring(0, index);
					}
				}

				let textwidth;
				let textheight;
				let fsize = 20;				
				while(true)
				{
					ctx2.font = fsize+'px sans-serif';
					let m1 = ctx2.measureText(name);
					let m2 = ctx2.measureText(provider);
					textwidth = Math.max(m1.width, m2.width);
					//textheight = (m1.fontBoundingBoxAscent + m1.fontBoundingBoxDescent)*3; // + barsize.height + 2;
					textheight = Math.max(m1.actualBoundingBoxAscent + m1.actualBoundingBoxDescent, m2.actualBoundingBoxAscent + m2.actualBoundingBoxDescent);
					//textheight = Math.max(m1.fontBoundingBoxAscent + m1.fontBoundingBoxDescent, m2.fontBoundingBoxAscent + m2.fontBoundingBoxDescent);
					
					if(textwidth8 && corh>8)
				{
					//console.log("b: "+textwidth+" "+textheight+" "+corw+" "+corh+" "+fsize);
					let x = mystartx + 2;
					let y = mystarty + Math.max((corh-textheight)/2, 2);
					this.drawProgressBar(x, y, textwidth, textheight, 'red', progress.progress/100, true, ctx2);
				}
			}
		}	
		
		// Draw range area.
		if(!this.calculating && this.range!=null)
		{
			if(canvas2==null)
			{
				canvas2 = this.createCanvas(canvas.offsetWidth, canvas.offsetHeight);
				ctx2 = canvas2.getContext('2d');
			}
			
			let rratio = this.range.width/this.range.height;
			let bratio = width/height;
			
			// Draw left and right boxes to show unused space
			if(rratiobratio)
			{
				let	drawheight	= this.range.width*sheight/swidth;
				let offset = (this.range.height-drawheight)/2;
				ctx2.fillStyle = "rgba(128,128,128,0.25)";
				ctx2.fillRect(srange.x, srange.y+offset, srange.width+1, -offset);
				ctx2.fillRect(srange.x, srange.y+srange.height, srange.width+1, -offset);
			}
		
			ctx2.strokeStyle = "white";
			ctx2.strokeRect(srange.x, srange.y, srange.width, srange.height);
		}
		
		if(canvas2!=null)
			ctx.drawImage(canvas2, 0, 0, canvas2.width, canvas2.height, 0, 0, width, height);
	}
	
	/**
	 * ctx: The draw context
	 * image: The image data to draw
	 * swidth: with in pixel of source picture
	 * sheight: height in pixel of source picture
	 * startx: where to start at target
	 * starty: where to start at target
	 * width: width of target
	 * height: height of target
	 * fill: fill target (resize) or paint pixel as is
	 */
	drawImage(ctx, image, swidth, sheight, width, height, fill, xoff, yoff)
	{
		let startx = Math.trunc((width-swidth)/2);
		let starty = Math.trunc((height-sheight)/2);
		xoff = xoff===undefined? 0: xoff;
		yoff = yoff===undefined? 0: yoff;
		
		let imgdata = new ImageData(image, swidth, sheight);
		if(!fill)
		{
			//ctx.putImageData(imgdata, startx, starty, xoff, yoff, swidth, sheight);
			let canvas2 = this.createCanvas(swidth, sheight);
			let ctx2 = canvas2.getContext('2d');
			ctx2.putImageData(imgdata, 0, 0);
			ctx.drawImage(canvas2, -xoff, -yoff, swidth, sheight, startx, starty, swidth, sheight);
		}
		else
		{
			let canvas2 = this.createCanvas(swidth, sheight);
			let ctx2 = canvas2.getContext('2d');
			ctx2.putImageData(imgdata, 0, 0);
			ctx.drawImage(canvas2, 0, 0, swidth, sheight, xoff, yoff, width, height);
		}
	}
	
	makeElementDragable(element, leftborder) 
	{
		var x1 = 0;
		var y1 = 0;
		var x2 = 0; 
		var y2 = 0;

		var moved = e =>
		{
			e = e || window.event;
	    	e.preventDefault();
	    	x1 = x2 - e.clientX;
	    	y1 = y2 - e.clientY;
	    	x2 = e.clientX;
	    	y2 = e.clientY;
			//console.log("to: "+x2+" "+y2);
	    	// set the element's new position:
			//var y = parseInt(element.style.top) || 0;
	    	//var x = parseInt(element.style.left) || 0;
			element.style.top = element.offsetTop-y1+"px";
	    	element.style.left = element.offsetLeft-x1+"px";
		}

		var md = e => 
		{
			//console.log("offsetx: "+e.offsetX+" "+e.clientX+" "+element.offsetWidth);
			
			// only dragable when at left border (in case leftborder true)
			if(leftborder && e.offsetX>0)
			{
				//console.log("not at border");
				return;
			}
			
			if(element.offsetWidth-e.offsetX < 20 && element.offsetHeight-e.offsetY < 20)
			{
				//console.log("at resize border");
				return;
	    	}

			e = e || window.event;
	    	//e.preventDefault(); // if used will hinder input fields from working
			x2 = e.clientX;
	    	y2 = e.clientY;
			//console.log("from: "+x2+" "+y2);
			
			// clean up document mouse listeners after mouse released
			var mu = e =>
			{
				document.removeEventListener("mouseup", mu);
				document.removeEventListener("mousemove", moved);
			};
			
			document.addEventListener("mouseup", mu);
			
			// watch now for movements
			document.addEventListener("mousemove", moved);
	  	}

		// listen on mouse clicks on that element
		element.addEventListener("mousedown", md);
	}
	
	getMethodPrefix() 
	{
		//return '/webjcc/invokeServiceMethod?cid=null'+'&servicetype='+'jadex.micro.examples.mandelbrot.IDisplayService'; cid=null leads to CID(null) :-()
		return '/webjcc/invokeServiceMethod?servicetype='+'jadex.micro.mandelbrot.IDisplayService';
	}
	
	initResults(data)
	{
		if(data.data==null)
			data.data = this.makeArray(data.sizeX, data.sizeY);
		this.data = data;
		this.data.image = new Uint8ClampedArray(data.sizeX*data.sizeY*4);
	}
	
	addProgress(part)
	{		
		if(this.progressdata==null)
			this.progressdata = {};//new HashSet();
				
		let key = this.getProgressKey(part);
		
		delete this.progressdata[key];	
		
		if(!part.finished)
			this.progressdata[key] = part;

		let len = Object.keys(this.progressdata).length;
				
		if(len>0)
			this.range = null;
				
		if(len==0)
			this.calculating = false;
		
		//console.log("progressdata len (before, after): "+len+" "+Object.keys(this.progressdata).length+" "+part.finished+" "+part.progress);
		
		//console.log("progress update");
		this.paint();
		//this.update();
	}
	
	getProgressKey(part)
	{
		let area = part.area;
		let key = 'x='+area.x+"y="+area.y+"w="+area.w+"h="+area.h;
		return key;
	}
	
	addDataChunk(data) //PartDataChunk 
	{
		// first chunk is empty and only delivers name of worker
		if(data.data===null || this.data===null)
		{
			console.log("Cannot add data chunk, no initial AreaData received");
			return; 
		}
		
		var chunk = data.data;
		var results = this.data.data;
		var image = this.data.image;
		
		var xi = data.area.x+data.xStart;
		var yi = data.area.y+data.yStart;
		var xmax = data.area.x+data.area.w;
				
		//console.log("chunk: "+xi+" "+yi+" "+xmax+" "+chunk.length);
				
		var cnt = 0;
		while(cnt=xmax)
			{
				xi=data.area.x;
				yi++;
			}
		}		
		
		//console.log("addDataChunk update");
		//this.update();
		this.paint();
	}
	
	makeArray(d1, d2) 
	{
    	var arr = [];
    	for(let i = 0; i < d1; i++) 
    	{
        	arr.push(new Array(d2));
    	}
    	return arr;
	}
	
	getColor(value)
	{
		let ret;
		if(value==-1)
		{
			ret = this.createColor(0, 0, 0);
		}
		else
		{
			ret = this.colors[value%this.colors.length];
		}
		return ret
	}
	
	drawPixel(data, width, x, y, color) 
	{
		this.drawPixel2(data, width, x, y, this.getRed(color), this.getGreen(color), this.getBlue(color), this.getAlpha(color));
	}
	
	drawPixel2(data, width, x, y, r, g, b, a) 
	{		
    	var index = x*4 + y*width*4;
	    data[index + 0] = r;
	    data[index + 1] = g;
	    data[index + 2] = b;
	    if(a)
	    	data[index + 3] = a;
	    else
	    	data[index + 3] = 255; // make NOT transparent 
	}
	
	drawProgressBar(x, y, w, h, color, percentage, complete, ctx)
	{
    	if(complete)
    	{
	    	ctx.beginPath();
	    	ctx.arc(h / 2 + x, h / 2 + y, h / 2, Math.PI / 2, 3 / 2 * Math.PI);
	    	ctx.lineTo(w - h + x, 0 + y);
	    	ctx.arc(w - h / 2 + x, h / 2 + y, h / 2, 3 / 2 *Math.PI, Math.PI / 2);
	    	ctx.lineTo(h / 2 + x, h + y);
	    	ctx.strokeStyle = '#000000';
	    	ctx.stroke();
	    	ctx.closePath();
	    }
    	
		let p = percentage * w;
    	if(p <= h)
    	{
      		ctx.beginPath();
      		ctx.arc(h / 2 + x, h / 2 + y, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
      		ctx.save();
      		ctx.scale(-1, 1);
      		ctx.arc((h / 2) - p - x, h / 2 + y, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
      		ctx.restore();
      		ctx.closePath();
    	} 
    	else 
    	{
      		ctx.beginPath();
      		ctx.arc(h / 2 + x, h / 2 + y, h / 2, Math.PI / 2, 3 / 2 *Math.PI);
      		ctx.lineTo(p - h + x, 0 + y);
      		ctx.arc(p - (h / 2) + x, h / 2 + y, h / 2, 3 / 2 * Math.PI, Math.PI / 2);
      		ctx.lineTo(h / 2 + x, h + y);
      		ctx.closePath();
    	}
    	ctx.fillStyle = color;
    	ctx.fill();
  	}
	
	createCanvas(width, height)
	{
		let canvas = document.createElement('canvas');
		let ctx = canvas.getContext('2d');
		//canvas2.style.background = "transparent";
		ctx.canvas.width  = width;
		ctx.canvas.height = height;
		return canvas;
	}
	
	// Determine how the image can be printed on screen
	// swidth/height: screen width/height (screen)
	// iwidth/height: image width/height (calculated)
	/*scaleToFit(swidth, sheight, iwidth, iheight)
	{
		var sratio = swidth/sheight;
		var iratio = iwidth/iheight;
		
		var drawstartx = 0;
		var drawstarty = 0;
		var drawendx = swidth;
		var drawendy = sheight;
		
		// Scale to fit height
		if(iratio<=sratio)
		{
			 var hratio	= sheight/iheight;
			 drawendy = iwidth*hratio;
			 drawstartx	= (swidth-drawendx)/2;
		}
		// Scale to fit width
		else if(iratio>sratio)
		{
			 var wratio = swidth/iwidth;
			 drawendy = iheight*wratio;
			 drawstarty	= (sheight-drawendy)/2;
		}
		return {x: Math.trunc(drawstartx), y: Math.trunc(drawstarty), width: Math.trunc(drawendx), height: Math.trunc(drawendy)};
	}*/
	
	createColor(r, g, b, a)
	{
		const color = a << 24 | r << 16 | g << 8 | b; 
		return color;
	}
	
	getAlpha(color)
	{
		return color >>> 24;
	}
	
	getRed(color)
	{
		return color >>> 16 & 0xff;
	}

	getGreen(color)
	{
		return color >>> 8 & 0xff;
	}

	getBlue(color)
	{
		return color & 0xff;
	}

	createColorBetween(start, end, diff)
	{
		let ret = this.createColor(
			this.getRed(start)+diff*(this.getRed(end)-this.getRed(start)),
			this.getGreen(start)+diff*(this.getGreen(end)-this.getGreen(start)),
			this.getBlue(start)+diff*(this.getBlue(end)-this.getBlue(start))
		);
		
		//console.log("start: "+start+" end: "+end+" diff: "+diff+" created: "+ret);
		
		return ret;
	}

	setColorScheme(scheme, cycle)
	{
		if(scheme==null || scheme.length==0)
		{
			this.colors	= [this.createColor[0xFF, 0xFF, 0xFF]];
		}
		else if(scheme.length==1)
		{
			this.colors	= scheme;
		}
		else if(cycle)
		{
			this.colors	= new Array(scheme.length*16);
			for(var i=0; i 
		{
			drag = false;
			
			if(!self.calculating)
			{
				if(e.button===2)
				{
	            	self.startdrag = self.getMousePosition(element, e);
	            	self.range = null;
					self.point = null;
					console.dir("startdrag: "+self.startdrag);
            	}
            	else if(e.button===0)
            	{
					self.point = self.getMousePosition(element, e);
				}
           	}
            else
			{
				self.startdrag = null;
			}
		}
		element.addEventListener('mousedown', downlis);
		
		let movelis = e => 
		{
			//console.log("mouse move: "+e.button);
			drag = true;
			
			if(self.startdrag!=null && e.buttons===2)
			{
	            self.enddrag = self.getMousePosition(element, e);
				console.dir('update, new enddrag: '+self.enddrag);
				self.paint();
				//self.update();
			}
			
			if(!self.calculating && self.point!=null && e.buttons===1)
			{
				let pos = self.getMousePosition(element, e); 
				
				let startx = this.getStartX();
				let starty = this.getStartY();
				
				//console.table("x1: "+self.point.x+" x2: "+pos.x);
				//console.table("y1: "+self.point.y+" x2: "+pos.y);
				
				let x1 = self.point.x;
				let x2 = pos.x;
				let y1 = self.point.y;
				let y2 = pos.y;
				
				if(!self.isFill())
				{
					if(x1startx+self.getImageWidth())
						x1 = startx+self.getImageWidth();
					if(x2>startx+self.getImageWidth())
						x2 = startx+self.getImageWidth();
					if(y1starty+self.getImageHeight())
						y1 = starty+self.getImageHeight();
					if(y2>starty+self.getImageHeight())
						y2 = starty+self.getImageHeight();
				}
				
				let range = {
					x: x1 
		{
			if(self.startdrag!=null && self.enddrag!=null)
			{
				self.dragImage();
			}
			
			self.startdrag = null;
			self.enddrag = null;
			
			//self.update();
			self.paint();
			//console.log("update drag");
		}
		element.addEventListener('mouseup', uplis);
		
		let wheellis = e =>
		{
			let pos = self.getMousePosition(element, e);
			let delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)))*-1;
			//console.log("wheel: "+delta);
			
			let percent = Math.abs(10*delta)*2;
			let factor;
			if(delta>0)
			{
				factor = (100+percent)/100;
			}
			else
			{
				factor = 100/(100+percent);
			}
			let x = pos.x;
			let y = pos.y;
			if(!self.isFill())
			{
				x = x-self.getStartX();
				y = y-self.getStartY();
			}
			else
			{
				x = x*self.getImageWidth()/self.getCanvasScreenWidth();
				y = y*self.getImageHeight()/self.getCanvasScreenHeight();
			}
			
			//self.checkAdaptSizeToCanvas();
			
			self.zoomImage(x, y, factor);
		}
		element.addEventListener("wheel", wheellis);
		
		let clicklis = e =>
		{
			//console.log("drag: "+drag);
			if(drag)
				return;
	
			let pos = self.getMousePosition(element, e); 
			
			if(e.button===2)
			{
				//self.calcDefaultImage();
			}
			else if(e.button===0 && !self.calculating && self.range!=null)
			{
				// Zoom when user clicked into range
				let srange = this.convertRangeToScreenValues(self.range); 

				if(pos.x>=srange.x && pos.x<=srange.x+srange.width
					&& pos.y>=srange.y && pos.y<=srange.y+srange.height)
				{
					//self.checkAdaptSizeToCanvas();
					self.zoomIntoRange();
				}
				else
				{
					self.range = null;
					console.log("update click");
					self.paint();
					//self.update();
				}
			}
		}
		
		element.addEventListener("click", clicklis);
	}
	
	getMousePosition(element, event)
	{
		let rect = element.getBoundingClientRect();
	    let x = event.clientX - rect.left;
	    let y = event.clientY - rect.top;
		return {x,y};
	}
	
	getImageWidth()
	{
		let results = this.data.data;
		let width = results.length;
		return width;
	}
	
	getImageHeight()
	{
		let results = this.data.data;
		let height = results[0].length;
		return height;
	}
	
	getCanvasWidth()
	{
		let canvas = this.shadowRoot.getElementById("canvas");
		let ctx = canvas.getContext("2d");
		return ctx.canvas.width;
	}
	
	getCanvasHeight()
	{
		let canvas = this.shadowRoot.getElementById("canvas");
		let ctx = canvas.getContext("2d");
		return ctx.canvas.height;
	}
	
	getCanvasScreenWidth()
	{
		let canvas = this.shadowRoot.getElementById("canvas");
		return canvas.offsetWidth;
	}
	
	getCanvasScreenHeight()
	{
		let canvas = this.shadowRoot.getElementById("canvas");
		return canvas.offsetHeight;
	}
	
	convertRangeToScreenValues(r)
	{
		let range = {...r}; // clone
		
		if(!this.isFill())
		{
			range.x = range.x+this.getStartX();
			range.y = range.y+this.getStartY();
		}
		else
		{
			range.x = range.x*this.getCanvasScreenWidth()/this.getImageWidth();
			range.y = range.y*this.getCanvasScreenHeight()/this.getImageHeight();
			range.width = range.width*this.getCanvasScreenWidth()/this.getImageWidth();
			range.height = range.height*this.getCanvasScreenHeight()/this.getImageHeight();
		}
		
		return range;
	}
	
	convertRangeToImageValues(r)
	{
		let range = {...r}; // clone
		
		if(!this.isFill())
		{
			range.x = range.x-this.getStartX();
			range.y = range.y-this.getStartY();
		}
		else
		{
			range.x = range.x*this.getImageWidth()/this.getCanvasScreenWidth();
			range.y = range.y*this.getImageHeight()/this.getCanvasScreenHeight();
			range.width = range.width*this.getImageWidth()/this.getCanvasScreenWidth();
			range.height = range.height*this.getImageHeight()/this.getCanvasScreenHeight();
		}
		
		return range;
	}
	
	getScreenRange()
	{
		return this.range==null? null: convertRangeToScreenValues(this.range);
	}
	
	getStartX()
	{
		return Math.trunc((this.getCanvasWidth()-this.getImageWidth())/2);
	}
	
	getStartY()
	{
		return Math.trunc((this.getCanvasHeight()-this.getImageHeight())/2);
	}
	
	dragImage()
	{
		console.log("dragImage: "+this.startdrag+" "+this.enddrag);
		
		let sw = this.getImageWidth();
		let sh = this.getImageHeight();
		//let drawarea = this.scaleToFit(sw, sh, iw, ih);
		
		let xdiff = this.startdrag.x-this.enddrag.x;
		let ydiff = this.startdrag.y-this.enddrag.y;
		let xp = xdiff/sw;
		let yp = ydiff/sh;
		
		let xm = (this.data.xEnd-this.data.xStart)*xp;
		let ym = (this.data.yEnd-this.data.yStart)*yp;
	 	let xs = this.data.xStart+xm;
		let xe = this.data.xEnd+xm;
		let ys = this.data.yStart+ym;
		let ye = this.data.yEnd+ym;
		
		this.startdrag = null;
		this.enddrag = null;
		//this.range = {x: xdiff, y: ydiff, width: sw, height: sh};

		let size = [this.data.sizeX, this.data.sizeY];
		if(this.isTrackSize())
			 size = this.adaptSize(this.data.sizeX, this.data.sizeY, this.getCanvasScreenWidth(), this.getCanvasScreenHeight());

		this.calcArea(xs, xe, ys, ye, size[0], size[1]);
	}

	// Zoom into the given location by the given factor.
	// x, y must be coordinates in image scale (not screen)
	zoomImage(x, y, factor)
	{
		//console.log("zoomImage "+factor);
		
		let sw = this.getImageWidth();
		let sh = this.getImageHeight();
		let iw = this.getImageWidth();
		let ih = this.getImageHeight();
		
		let mx = Math.min(sw, Math.max(0, x));
		let my = Math.min(sh, Math.max(0, y));
		let xrel = mx/sw;
		let yrel = my/sh;

		let wold = this.data.xEnd-this.data.xStart;
		let hold = this.data.yEnd-this.data.yStart;
		let wnew = wold*factor;
		let hnew = hold*factor;
		let wd = wold-wnew;
		let hd = hold-hnew;
		
		let xs = this.data.xStart+wd*xrel;
		let xe = xs+wnew;
		let ys = this.data.yStart+hd*yrel;
		let ye = ys+hnew;
		
		// Set range for drawing preview of zoom area.
		let xdiff = sw - sw*factor;
		let ydiff = sh - sh*factor;
		
		// range is set in image scale
		this.range = {x: Math.trunc(xdiff*xrel), y: Math.trunc(ydiff*yrel),
			width: Math.trunc(sw*factor), height: Math.trunc(sh*factor)};
		
//		zoomIntoRange();

		let size = [this.data.sizeX, this.data.sizeY];
		if(this.isTrackSize())
			 size = this.adaptSize(this.data.sizeX, this.data.sizeY, this.getCanvasScreenWidth(), this.getCanvasScreenHeight());

		this.calcArea(xs, xe, ys, ye, size[0], size[1]);
	}
	
	zoomIntoRange()
	{
		//console.log("zoomIntoRange: "+this.range);
		//let sw = this.range.width;
		//let sh = this.range.height;
		
		let iw = this.getImageWidth();
		let ih = this.getImageHeight();
		
		let x = this.range.x/iw;
		let y = this.range.y/ih;
		let x2 = x + this.range.width/iw;
		let y2 = y + this.range.height/ih;
		
		// Original bounds
		let ox = this.data.xStart;
		let oy = this.data.yStart;
		let owidth = this.data.xEnd-this.data.xStart;
		let oheight	= this.data.yEnd-this.data.yStart;

		// adapt cutout to image data size				
		let neww;
		let newh;
		let rw = this.range.width/this.range.height;
		let rh = this.range.height/this.range.width;
		if(rh<1)
		{
			neww = iw;
			newh = Math.trunc(rh*ih);
		}
		else
		{
			neww = Math.trunc(rw*iw);
			newh = ih;
		}
		
		// adapt image data size to screen size
		let size = [neww, newh];
		if(this.isTrackSize())
			 size = this.adaptSize(neww, newh, this.getCanvasScreenWidth(), this.getCanvasScreenHeight());
		
		this.calcArea(ox+owidth*x, ox+owidth*x2, oy+oheight*y, oy+oheight*y2, size[0], size[1]);
	}
	
	adaptSize(w1, h1, w2, h2)
	{
		let rw = w2/w1;
		let rh = h2/h1;
		let r = Math.min(rw, rh);
		return [r*w1, r*h1];
	}
	
	getAlgorithmDefaultSettings(name)
	{
		return new Promise((resolve, reject) => 
		{
			fetch('mandelbrotdisplay/getAlgorithmDefaultSettings?a=' + name)
			.then(response => 
			{
				if(!response.ok) 
			      throw new Error('Network response was not ok');
			    return response.json();
			})
			.then(data => 
			{
			    console.log("fetching default settings finished: " + data);
			    resolve(data);
			})
			.catch(error => 
			{
			    console.error('There was a problem with the fetch operation:', error);
			    reject(error);
			});
			
			/*axios.get('/mandelbrotdisplay/getAlgorithmDefaultSettings?a='+name, this.transform)
			.then(function(response)
			{
				console.log("fetching default settings finished: "+response.data);
				resolve(response.data);
			})
			.catch(ex => 
			{
				console.log(ex);
				reject(err);
			});*/
		});
	}
	
	getAlgorithms()
	{
		return new Promise((resolve, reject) => 
		{
			fetch('mandelbrotdisplay/getAlgorithms')
			.then(response => 
			{
				if(!response.ok) 
			      throw new Error('Network response was not ok');
			    return response.json();
			})
			.then(data => 
			{
			    console.log("fetching algos finished: " + data);
			    resolve(data);
			})
			.catch(error => 
			{
			    console.error('There was a problem with the fetch operation:', error);
			    reject(error);
			});
		});
		
	}
		
	calcArea(x1, x2, y1, y2, sizex, sizey, algo, max, chunks, tasksize)
	{
		let data = {};
		data.xStart = x1;
		data.xEnd = x2;
		data.yStart = y1;
		data.yEnd = y2; 
		data.sizeX = sizex;
		data.sizeY = sizey;
		
		if(algo)
			data.algorithmClass = algo;
		else if(this.data!=null)
			data.algorithmClass = this.data.algorithmClass;
		
		if(max)
			data.max = max;
		else if(this.data!=null)
			data.max = this.data.max;
		
		if(chunks)
			data.chunkCount = chunks;
		else if(this.data!=null)
			data.chunkCount = this.data.chunkCount;
			
		if(tasksize)
			data.taskSize = tasksize;
		else if(this.data!=null)
			data.taskSize = this.data.taskSize;
		
		data.displayId = this.displayid;
		
		//DisplayPanel.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		this.calculating = true;
		
		//console.log("update calcArea");
		this.paint();
		//this.update();
		
		console.log("request data: "+JSON.stringify(data));
		
		fetch('mandelbrotgenerate/generateArea?a=' + encodeURIComponent(JSON.stringify(data)))
		.then(response => 
		{
    		if(!response.ok) 
      			throw new Error('Network response was not ok');
			//console.log("generateArea finished: " + response.ok);
    		self.calculating = false;
    		//self.handleDisplayUpdate(response);
    		//DisplayPanel.this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
  		})
  		.catch(error => 
  		{
    		console.error('There was a problem with the fetch operation:', error);
    		self.calculating = false;
  		});
		
		/*axios.get('/mandelbrotgenerate/generateArea?a='+JSON.stringify(data), this.transform)
		.then(function(response)
		{
			console.log("generateArea finished: "+response.data);
			self.calculating = false;
			//self.handleDisplayUpdate(response);
			//DisplayPanel.this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		})
		.catch(ex => 
		{
			console.log(ex);
			self.calculating = false;
		});*/
	}
	
	generateArea(e)
	{
		var algo = this.shadowRoot.getElementById("algorithm").value;
		if(algo)
			algo = {value: algo};
		
		var xmin = this.shadowRoot.getElementById("xmin").value;
		var xmax = this.shadowRoot.getElementById("xmax").value;
		var ymin = this.shadowRoot.getElementById("ymin").value;
		var ymax = this.shadowRoot.getElementById("ymax").value;
		var sizex = this.shadowRoot.getElementById("sizex").value;
		var sizey = this.shadowRoot.getElementById("sizey").value;
		var max = this.shadowRoot.getElementById("max").value;
		var chunks = this.shadowRoot.getElementById("chunks").value;
		var tasksize = this.shadowRoot.getElementById("tasksize").value;
		
		this.calcArea(xmin, xmax, ymin, ymax, sizex, sizey, algo, max, chunks, tasksize);
	}
	
	isTrackSize()
	{
		return this.shadowRoot.getElementById("tracksize").checked;
	}
	
	resetSettings(e)
	{
		this.setDefaultSettings().then(x =>
		{
			this.generateArea();
		})
		.catch(err =>
		{
			console.log(err);
		});
		this.range = null;
	}
	
	setSettings(data)
	{
		if(data.algorithmClass!=null)
			this.shadowRoot.getElementById("algorithm").value = data.algorithmClass.value;
		this.shadowRoot.getElementById("xmin").value = data.xStart;
		this.shadowRoot.getElementById("xmax").value = data.xEnd;
		this.shadowRoot.getElementById("ymin").value = data.yStart;
		this.shadowRoot.getElementById("ymax").value = data.yEnd;
		this.shadowRoot.getElementById("sizex").value = data.sizeX;
		this.shadowRoot.getElementById("sizey").value = data.sizeY;
		this.shadowRoot.getElementById("max").value = data.max;
		this.shadowRoot.getElementById("chunks").value = data.chunkCount;
		this.shadowRoot.getElementById("tasksize").value = data.taskSize;
	}
	
	setDefaultSettings(e)
	{
		let self = this;
		return new Promise(function(resolve, reject)
		{
			let elem = self.shadowRoot.getElementById('algorithm');
			let value = elem.options[elem.selectedIndex].value;
			self.getAlgorithmDefaultSettings(value).then(data =>
			{
				self.setSettings(data);
				resolve();
			})
			.catch(err =>
			{
				console.log(err);
				reject(err);
			});
		})
	}
	
	adaptSizeToCanvas(e, self)
	{
		if(self==null)
			self = this;
		
		let canvas = this.shadowRoot.getElementById("canvas");
		self.shadowRoot.getElementById("sizex").value = canvas.offsetWidth;
		self.shadowRoot.getElementById("sizey").value = canvas.offsetHeight;
	}
	
	isFill()
	{
		let fill = this.shadowRoot.getElementById("fill").checked;
		return fill;
	}

	getCss() 
	{
		return `
			.dragable {
				padding: 10px;
				position: fixed;
				left: 50%;
    			top: 50%;
    			transform: translate(-50%, -50%);
				width: 30%;
			  	background-color: #00000066;
			  	border: 1px solid #d3d3d3;
				z-index: 1;
		 		resize: both;
    			overflow: hidden;
    			color: white;
			}
			.dragable2 {
				position: fixed;
				width: fit-content;
				height: fit-content;
				resize: both;
				overflow: hidden;
				background-color: yellow;
			}
			.grid {
				display: grid;
				grid-template-columns: max-content auto;
				grid-gap: 0.5em;
			}
			.grid2 {
				display: grid;
				grid-template-columns: auto auto;
			}
			.fitcontent {
				width: fit-content;
			}
			.fitcontentheight {
				height: fit-content;
			}
			.floatright {
				float: right;
			}
			.margintop {
				margin-top: 0.5em; 
			}
			.jadexbtn {
				background-color:#2a6699;
				border-radius:6px 6px 6px 6px;
				font-weight:bold;
				font-weight:600;
				padding-top:10px;
				padding-bottom:10px;
				border: 0px;
				color: #fff;
			}
			.jadexbtn:disabled,
			.jadexbtn.disabled {
				border: 1px solid #999999;
				background-color: #cccccc;
		  		color: #666666;
			}
			.leftborder {
				border-left: 10px solid red; 
			}
			.w100 {
				width: 100%;
			}
			.h100 {
				height: 100%;
			}
			.checkboxleft {
				width: fit-content;
				margin-left: 0px;
			}
			.block {
				display: block;
			}
		    `;
		// display block for canvas to
		// avoid strange 5px bottom :-( https://stackoverflow.com/questions/15807833/div-containing-canvas-have-got-a-strange-bottom-margin-of-5px
	}
	
	getHtml() 
	{
		return `
			

Factals

`; } } if(customElements.get('jadex-mandelbrot') === undefined) customElements.define('jadex-mandelbrot', MandelbrotElement);




© 2015 - 2025 Weber Informatics LLC | Privacy Policy