An Assortment of Square and Circular XY Pads!
Ok here it is. I put five XY pads and some fun knobs into one snippet for you all to enjoy. I hope you find the code interesting. It should be possible to cut out the parts you need and put them in your projects. I decided not to use broadcasters to keep things simple, but I did have some difficulty linking the X and Y knobs to processors in the module tree because of this. Let me know what you all think.

// An Assortment of Square and Circular XY Pads
// By Jack Huppert
//
// Helpfull Reading
// Analytical Methods for Squaring the Disc by Chamberlain Fong
// 	https://arxiv.org/pdf/1509.06344
// Elliptification of Rectangular Imagery By Chamberlain Fong
// 	https://arxiv.org/pdf/1709.07875
// Square/Disc mappings by Marc B. Reynolds
// 	https://marc-b-reynolds.github.io/math/2017/01/08/SquareDisc.html 
Content.makeFrontInterface(810, 250);
// init X and Y knobs
const knbX = Content.getComponent("knbX");
const knbY = Content.getComponent("knbY");
knbX.setControlCallback(onknbXYControl);
knbY.setControlCallback(onknbXYControl);
// button to enable updating all XY pads when one XY pad is changed
const btnLink = Content.getComponent("btnLink");
reg curRadius = 8;
const XY_MARGIN = 5;
const XY_INSET = 5;
// init XY pads	
const var pnlXY = [];
for(i=0; i<5; i++){
	pnlXY[i] = Content.getComponent("pnlXY"+i);
	pnlXY[i].setControlCallback(onpnlXYControl);
	pnlXY[i].setMouseCallback(pnlXYMouseCallback);
	pnlXY[i].setPaintRoutine(paintpnlXY);
	pnlXY[i].data.shape = i;
	pnlXY[i].data.u = 0;
	pnlXY[i].data.v = 0;
	pnlXY[i].data.x = 0;
	pnlXY[i].data.y = 0;
}
//just for fun knob to change the cursor size
inline function onknbCursorSizeControl(component, value)
{
	curRadius = value;
	for(i=0; i<5; i++){
		pnlXY[i].repaint();
	}
};
Content.getComponent("knbCursorSize").setControlCallback(onknbCursorSizeControl);
//just for fun knob to change the aspect ratio
inline function onknbAspectRatioControl(component, value)
{
	local h = Math.range(150*value,50,150);
	local w = Math.range(150*(1/value),50,150);
	for(i=0; i<5; i++){
		pnlXY[i].set("height",h);
		pnlXY[i].set("width",w);
		pnlXY[i].repaint();
	}
};
Content.getComponent("knbAspectRatio").setControlCallback(onknbAspectRatioControl);
// Update knobs and repaint the panel on control
inline function onpnlXYControl(component, value)
{
	local x = component.data.x;
	local y = component.data.y;
	knbX.setValue(x);
	knbY.setValue(y);
};
// Mouse handling
inline function pnlXYMouseCallback(event)
{
	if (!event.drag && !event.clicked){
		return;
	}
	// get the mouse position in the right coordnate system	
	local b = this.getLocalBounds(0);
	local m = XY_MARGIN+XY_INSET+curRadius;
	this.data.u = (event.x-m)/(b[2]-2*m);
	this.data.v = (b[3]-event.y-m)/(b[3]-2*m);
	
	// clamp u and v to the bounds
	if(this.data.shape<=0||this.data.shape>4){//bounds = square
		this.data.u = Math.range(this.data.u,0,1);
		this.data.v = Math.range(this.data.v,0,1);
		// square needs no further calculations so just return
		this.data.x = this.data.u;
		this.data.y = this.data.v;
		if(btnLink.getValue()){ //
			squareToCircle(this.data.x,this.data.y);
		}else{
			this.changed();
		}
		return;
	}// else bounds = circle
	
	local uN = this.data.u-.5; //uN is -.5 to .5 if within the circle
	local vN = this.data.v-.5; //vN is -.5 to .5 if within the circle
	local dist = Math.sqrt(uN*uN + vN*vN); 
	if(dist>.5){ 
		this.data.u = uN *.5 / dist + .5;
		this.data.v = vN *.5 / dist + .5;
		dist=.5;	
	}//dist is now 0 to .5
	
	// now convert (u,v) --> (x,y) depending on the transformation
	if(this.data.shape==1){ // radial stretching (2017 Marc B. Reynolds)
		//[x,y] = sqrt(u^2 +v^2)/max(|u|,|v|) * [u,v]
		//we can cheat and use uN, vN, and dist again from above.
		this.data.x = (uN * dist/Math.max(Math.abs(uN),Math.abs(vN)+.00000001))+.5;
		this.data.y = (vN * dist/Math.max(Math.abs(uN),Math.abs(vN)+.00000001))+.5;
		
	}else if(this.data.shape==2){ // elliptic grid (2005 Philip Nowell)
		//x = .5*(sqrt(|2+u^2-v^2+sqrt(8)u|)-sqrt(|2+u^2-v^2-sqrt(8)u|))
		//y = .5*(sqrt(|2-u^2+v^2+sqrt(8)v|)-sqrt(|2-u^2+v^2-sqrt(8)v|))
		uN = this.data.u*2-1; //uN is now -1 to 1
		vN = this.data.v*2-1; //vN is now -1 to 1
		
		local SQRT8 = Math.sqrt(8);
		local temp1 = Math.abs(2 + (uN+SQRT8)*uN - vN*vN);
		local temp2 = Math.abs(2 + (uN-SQRT8)*uN - vN*vN);
		this.data.x = .25*(Math.sqrt(temp1) - Math.sqrt(temp2))+.5;
		temp1 = Math.abs(2 - uN*uN + (vN+SQRT8)*vN);
		temp2 = Math.abs(2 - uN*uN + (vN-SQRT8)*vN);
		this.data.y = .25*(Math.sqrt(temp1) - Math.sqrt(temp2))+.5;
		
	}else if(this.data.shape==3){ //FG-Squircle (2014 Chamberlain Fong)
		//x = sgn(uv)/(v*sqrt(2)*sqrt(u^2+v^2-sqrt((u^2+v^2)(u^2+v^2-4u^2v^2)))
		//y = sgn(uv)/(u*sqrt(2)*sqrt(u^2+v^2-sqrt((u^2+v^2)(u^2+v^2-4u^2v^2)))
		uN = this.data.u*2-1; //uN is now -1 to 1
		vN = this.data.v*2-1; //vN is now -1 to 1
		
		local temp1 = uN*uN + vN*vN;
		local temp2 = Math.sqrt(temp1*(temp1 - 4*uN*uN*vN*vN));
		temp2 = Math.sign(uN*vN) * Math.sqrt(temp1 - temp2)/Math.sqrt(2);
		this.data.x = vN==0 ? this.data.u : .5*temp2/vN + .5;
		this.data.y = uN==0 ? this.data.v : .5*temp2/uN + .5;
		
	}else if(this.data.shape==4){ //Concentric (1997 Peter Shirley and Kenneth Chiu)
		//x = u^2>v^2 ? sgn(u)sqrt(u^2+v^2) : 4/PI*sqrt(u^2+v^2)atan(u/|v|)
		//y = u^2>v^2 ? 4/PI*sqrt(u^2+v^2)atan(v/|u|) : sgn(v)sqrt(u^2+v^2)
		uN = this.data.u*2-1; //uN is now -1 to 1
		vN = this.data.v*2-1; //vN is now -1 to 1
		
		local temp = Math.sqrt(uN*uN + vN*vN);
		if(uN*uN>vN*vN){
			this.data.x = .5*temp*Math.sign(uN) + .5;
			this.data.y = 2/Math.PI*temp*Math.atan(vN/(Math.abs(uN)+.00000001)) + .5;
		}else{
			this.data.x = 2/Math.PI*temp*Math.atan(uN/(Math.abs(vN)+.00000001)) + .5;
			this.data.y = .5*temp*Math.sign(vN) + .5;
		}
	}
	
	if(btnLink.getValue()){
		squareToCircle(this.data.x,this.data.y);
	}else{
		this.changed();
	}	
	return;
}		
// on paint function for all XY pads
inline function paintpnlXY(g)
{	
	g.fillAll(this.get("bgColour"));
	
	// Calculate and store the cursor's XY position 
	local b = this.getLocalBounds(XY_MARGIN);
	local u = this.data.u * (b[2]-2*(curRadius+XY_INSET)) + XY_MARGIN + XY_INSET;
	local v = (1-this.data.v) * (b[3]-2*(curRadius+XY_INSET)) + XY_MARGIN + XY_INSET;
	// draw the xy area outline
	g.setColour(this.get("itemColour"));
	local bs = this.get("borderSize");
	if(this.data.shape==0){
		g.drawRect(b, bs);
	}else{
		b = this.getLocalBounds(XY_MARGIN+bs/2);
		g.drawEllipse(b, bs);
	}
	
	// draw the XY pad cursor
	g.setColour(this.get("itemColour2"));
	g.fillEllipse([u,v,curRadius*2,curRadius*2]);
}
// update all the XY pads when the knobs are changed
inline function onknbXYControl(component, value)
{
	local x = knbX.getValue();
	local y = knbY.getValue();
	squareToCircle(x,y);
}
// helper function to convert (x,y) --> (u,v) for all XY Panels
inline function squareToCircle(x,y){
	//square
	pnlXY[0].data.u = x;
	pnlXY[0].data.v = y;
	
	//radial stretching inverse  (2017 Marc B. Reynolds)
	local xN = (x*2-1); //xN is -1 to 1
	local yN = (y*2-1); //yN is -1 to 1
	local temp = Math.max(Math.abs(xN),Math.abs(yN));
	local dist = Math.sqrt(xN*xN + yN*yN +.00000001); //dist is (0,sqrt(2)]
	pnlXY[1].data.u = .5*xN*temp/dist +.5;
	pnlXY[1].data.v = .5*yN*temp/dist +.5;
	
	//elliptic grid inverse  (2005 Philip Nowell)
	//u = x*sqrt(1-.5*y^2)
	//v = y*sqrt(1-.5*x^2)
	//use xN and yN from above
	pnlXY[2].data.u = .5*(xN * Math.sqrt(1-yN*yN*.5))+.5;
	pnlXY[2].data.v = .5*(yN * Math.sqrt(1-xN*xN*.5))+.5;
	
	//FG-Squircle (2014 Chamberlain Fong)
	//u = x*sqrt(x^2+y^2-x^2*y^2)/sqrt(x^2+y^2)
	//v = y*sqrt(x^2+y^2-x^2*y^2)/sqrt(x^2+y^2)
	//use xN, yN, and dist from above
	temp = Math.sqrt(xN*xN + yN*yN - xN*xN*yN*yN)/dist;
	pnlXY[3].data.u = .5*xN*temp + .5;
	pnlXY[3].data.v = .5*yN*temp + .5;
	
	//Concentric (1997 Peter Shirley and Kenneth Chiu)
	//u = |x|>=|y| ? x*cos(PI/4*y/x) : y*sin(PI/4*x/y)
	//v = |x|>=|y| ? x*sin(PI/4*y/x) : y*cos(PI/4*x/y)
	//use xN and yN from above
	temp = Math.PI/4;
	if(Math.abs(xN)>=Math.abs(yN)){
		pnlXY[4].data.u = .5*xN*Math.cos(temp*yN/xN) + .5;
		pnlXY[4].data.v = .5*xN*Math.sin(temp*yN/xN) + .5;
	}else{
		pnlXY[4].data.u = .5*yN*Math.sin(temp*xN/yN) + .5;
		pnlXY[4].data.v = .5*yN*Math.cos(temp*xN/yN) + .5;
	}
	
	//set x and y as well
	for(i=0; i<5; i++){
		pnlXY[i].data.x = x;
		pnlXY[i].data.y = y;
		pnlXY[i].changed();
	}
}
HiseSnippet 3686.3oc2ZszbabbDdgjfKQ32U4bMdBqTN6h2.DTjxzTlRTR1LVDFVT1EYoh10hcG.rQK1EdeQrQjw9fSk+F4d9Cji9OPNlS4RtjS4R9G3z8L6iYwtfuRbpJ1UhM2Y5tmt+5tmo6Ay.GaMpqqsiToJOKbFUpzqU9fPKuI6NQ0vRZuGhCrqgiluopygGI8fvYpttTcoRkt4GgTTZkaIw9m+0G9.USUKMZ5PRRegsgF8IFSM7RGcvNehgo4iU0oOyXp.081YOMaqcsMs8As4lkaKMSU6Epio8UQxtQYoOV0chTopk06zqynM0Tuylc50USq6laz8tanQUGMhdmNquQuMG0ajpV6tRkdkGoa3Y6bfmpG0Upzsdfsd3ASrOwhu.eggqwPSJ9QGoCfUlO7isM0QSDGUZ2IFl5ChgIWIPJCRAsaxAs2o79F5FIimBduEaBRJGh.XoajU8tYF0qin50VP8JPkJInR2hqRuc4CzbLl4kNCpOuZ48r7nN.7PynJbZktwe5cK2pE49Vj6ir3MkZ4QrGQN3q8UcnDUKcRbv.4viHCT0cq.z+fPxuF7UjO1e1LpiGLDN5GSMmMx2zj7Tpptg03JLIqZF5YnoZR1m5MwV2kLx1gKefDh2DJ4gFtZjggD.EmNj5XBXI4w1b9WYhm2L22uUKUm4FAMscF2Zl9nVcVu8ca19Nq0qGRziLMAiwXDrLdF1VnA7Tplmp0Xlhu2THnxID05qxRrAtDar4FqiDwAjVLUcp5rYft6h579pNZjGzDVuPKHJxMi.mBS1XXCmn4ZN1vah+vlF1vLdSZ0scmMZ0tSq1a1hKdT5Mm3M0jToxt1feyxq4T0WPerC7QheTdyNsqS5tdakspfKmgkgG4Plu5HxKrrG5VAxqb8f+d3gjsIwRZL0aW6oyrsfOjWEmbUPBIjdz4Q5QHoHKMcwor7brM2U0zbHDEHaagybTzvbBO5RQHp9C887.elmMgZoBQ+D+Y5faDhM.1vftYPPG4jITvuZQiFfX3Rzl.NXpdjELzy5IFVuXoFQz7ncTwgNln467THJ02E3XyXX3vi9p8u+S+n85CCttvf60+fG8L9XBPNW0VIhr.HRalk4gHN97i2pBDlKarc6sHFev5v+pVMkWVYEFAO233kplLBVslAnlIDWLTxlMEJyP8919tzDZYyjYnEoe.jO38Tae.1oxyvOXylgLvon1zch5LJn7F4lwGFsctQCJbz4ENZHezyPD923CHJtQwHeKVLMFevc3rsL.uGrcEw032RqXXYBpMRoFO6Gix1kQvAv7QPjrVLHWGbUl9TkJf+PLJfMJnUE53R0UGJCfjQv4rJmsUkklzjpCqprzzgbJJOs3h..U2YvNbDGbCuhQf6yn3oHAmKDXZiaNOA.f8gMkZ5fKgLr+ZUFI0Wuc8NrsZhH7j7DJ2oEWbBDeAfHfFxqNgZLdh2p0mfLrvbmXn6MY05mjcpKO1KX8mC3mGiPzmkh+43tPT99or8ViVaF5OS0hZBvLQiyUAd.wzyyC3wbgjoiROR.6v7SFBSFuQ7WfxRdtBeniRGJDF5rXKgk4SfPGcS7P4EU076NHSCfkioiFiHx+B1mM0cTGSdu2iD8olog1Kn5L2pC0y2wh4SprBrhf2fASSYq7LaWC1JAm4hi5fdcvrrczsPH1Mz0iNckXadHXydSLbQe5SvQdfsuktqrPL3TfjjspqEu+bsjjYfPlDR1XhaQMm2XpRK4gOu6wM5VcpRFxvcpfoV63FbZCincsDZYlllo5zYDeVDQ.lShVzPlFhnkbp.YaU9Aa29zSWXr60S4ksZw4AVTW1Q+.JlUkExwDlnNjfwRIxp3ERbPBwfdyWEhEkBKpkM39c.M2g.vIVcG5ebIt1D19Nb+YlUYdrWgqGYUgvLSFfSBPQzItnejGUpn7RBTo3JqrBWadlMVaoonNOutfTY59YTSWJFjwWunC8k4ykI1CrRjVRBxpwjdkjHK+9YMhFMgslZ0BFFpl.9.cmv+Fh4OAJTKJZMVHbQDjUDAQhH3JHBcC.gibYtesimre+pfJTCjc0f9JaQXwQHU2q45.hkKx.HtJrFs3RpFrd4CHBJjF7isg+bEFZwlx.iFNgzlq3QA43HvNaAPo8DY+5AJjFMtGQdd8PEhNcF0BqtG29CsNOHtyE1teJKHpnjfs2tCyyCmVoa.HfqG3zzlfxPFqANWQzJrX1mCq2wrDDDj9xtjZAeYWEnz44xm5eZ8SCNUgTk7bP+NlQ+I.RqBaIOgp5wxOwMe76Ca4B+e7alAqNFq8eji8Th5P6.Zybg4xH.yHtEyIgKH6OTG5ByoTO4CvcUqYa9+zQA960ymWHG7elzpvy.HEAqc4vJk29iFYrigNBosWmL.ZYzXFou8Ivrb7DsslqWUlgmm1sF.oM.DsF66MU7OUowBS0HcJtHByJhF.c0DDQPpHhmpQ5TnHVLErZ2FcRyAw3tFcvPwN.sKlqESaPAzB+Od10Ae1Se1lYRu1jsUAeV3blYchmEwbHnB820Xrof4gMhyCyvT2BXpQwLkMXpYW.tRUFlB.oSjrC0MM3IuF1fDuCADhDqowKVdcKC4MVf7LglWUc67BEWiEJ93OpAzIKa6NVpcubMbmFJ5N1R1O.NiMnJas5pTMNSOMvI9Kkjg6A+ANfPDYhj7u9R5G8.yXGalc6WRPVp6nJ++.NkdUYbVkGnk246Zfn.aRX+lEDCvO2U1JchtEDsFze6saS9PQff79XFOiaz.yebSHynVju.Q97S467hg5whgfZl0fBvbf8yj6b26tAY.0CJT4fIFNlzP1F4eB0xh5MABtL7SCn.248.+InFr.BEwX.EPc50ZvdYBLTf0FHrEdRRRvTpTVB8AsfSeP4gqRP1U4+UARmW8C7huXCdO9PoUOktsD20TULzQIwIsf2sKOpAPiTd3PQ+VYNKS73qDgsPAbIpvRkpunTCVhTWbqrb1Sff8bF1YxJUVVQoUtJEjlXN4JG8LXEhqF8Lvgg8dg8Ww5ZLocKrmdgK1Je6XI29hLrY4KAYNt4HCSy6aZJG2Vj7pCGyu27UUR5LY2nJ442Zqqmsi3Uk7qbYqXbqXWTyVIcWk1zke13ZXSl3NojSZ7JoWLlmJ81zpkbIZIhi0uUmFBQ+JbQt1UWjL6G5O8DlAOG1jvgpRr88PnEAPV++HdI.gFP.iHHFAHtBHBfyPSpznqvYqhqssMK.ZL1e7I3sNKOrNHkLgJWHJWanaK99wb4vtPaWpfnhbxIFYz0fxctWrI1kai7PoXoikNWOAnq1U7uOVI5l33WGKkEyltvQWHKNPzMj.QawWHagWG0k9xPXWtQZ5Yl6BgcKGhyUYwLWrGkXMeB0bF0IUOvaPKtqFVuLrtZX82HjUN.uam74kErNuDcIwsuyuip1B2F57sVbTLlOLJeMeuPFnpAGMt7lhh.I7TC443gEJ3oEy4MeFeRQDZwHJLgnvhHR7njL8kLWrujv9B4G45dcd+pywrwv9Ug0PXuZbUiawTtc8nZNNNFS5HfTvt2fXPsgyAudyrzEvoKLOcHZlsEHQjrfdgfCiQ2C+r8NMPoxN6FN3EcPBiOOZbrSRvJwMVAiLs4wXkraViA.kL0f0oACcfNxiqkNKaQ1FfzKvFCbEXC0kKUM1YLQvJpAVXC3+xrzVhitncewDyAi5.RHzVsHljqBkrwHMHbyh8kBySlfHqUXTQ7A4YoIaDQLMnFd0KgjiWmN+z6s8ogmBE+Muplsq7f8Z0qZXq4X4d.9XXwGYdqvDXKCOITjvShTh4Y4gRhvFxB+7Fwbx6scljxzaTuWNXiQHt3rxhB6C6RjVOTVlBxxDZCEvTxgYEthgKx779vVNWvJFtnZlkony7fC1fyEX3EQEN2AxguvedgjhLmuUtgCi1DNc3LEwU4Lgys5a6Q+TKY1ITvgJjEmZznBmK5jNSpSgSiOCBmyiQYKeLgV77QjPoR2J66Gn7xe+.hOuAM9ORh.g1V6YY38oynVK6QOHE8KqHAqRjVAj5wdoAuQzKM3Are4VICcoRqTNpvZIlJK9JQjVF6GXZ.0VwX+UJim5mg2uc9e3eb448nL798+ke1e6Rw6aUN6OBz0S6eyxY9c7VTHjcj978dHD5guaiHXEfZ7gSXfdwROjFXnQ4uhiUJ+Pp6K7rmAhM4WzRpzqxW7WO9Mdf0nvV6aWlEF2VZN3nJUAVtvzU9WtC6mQKYfue3N7eyMwQfxdrOI9m9wEsFnUCR5.oEQhOMndcuau6dmM5d20ElnaxLs6rd6tRoUMmpK2dm3dVfwdke3G9g2gM7UvF6f1Xj31gtyOYsytB14ez6mt14Zh14u6mt1YOA6bv28+u1Y4r14STGlXmr+VL+TpmncpuiGctGRH+cNckDqX5v2NpHw9ZkOHoKpqjnEi.2wuHQWobzaKS6JIXQW9NeSQB9MKmTGO9F0tRRecwDmuqXDIsD3hD84dB9x7hFQB+MJydnUQOGvyS74Ogetn3Rk82ONIB4PxmXYO7pH0iVlWTPpGkWp29xWUvRxeSj+qWVj5o7WI6696+me1e9m+W+PnwZwrroF55lzAQWD2BkZjnbqboK1HU3+8uIqtsrUhriqGcFO6ub4NMaGqwwJRFMtWZHU5hJk+4m9Vk22VGu+wruFV7MCGMApDhOAU7AMZAJWn3Z+esmH6kUEe6xCLfsNJVGuQA5HTE2OF5XzCK90K+nQifPoTE7Vke7g+37Jhk3u3uwPSXNFX8i88md.b.fFEVcK7VvvZJuAdJ.+6132HBb.0Rm8AbNwODMYG76RQS1IdRoopZN1eUz6jBe5x2lMBnSVrm48Jk2G+lzYwxlkfvRciuRSKqnxwX2qKiqccYr20kw0utLdmqKiabcYbyKlQ7gteeeOa9C+.HX+AOh0lSoROh8RdYQqR+aPONMfL
 

 ). The solution to your problem is that you need to define a transformation from Knobspace (x,y) to Circlespace (angle, distance) and back. The pixel coordinates of the XY cursor can't be transformed into the knob values in a linear way so I used some trigonometry to make it work.
). The solution to your problem is that you need to define a transformation from Knobspace (x,y) to Circlespace (angle, distance) and back. The pixel coordinates of the XY cursor can't be transformed into the knob values in a linear way so I used some trigonometry to make it work.
