Circle through three points

Hello,

I am looking for an AFL for a CIRCLE that passes through three specified points on a chart. A paid AFL will be nice to have. I searched around the Forum for appropriate category. A category for Recommendations for Service Providers seems to exist at Third party services, blogs, courses, books, add-ons ,However, I could not find a category for service seekers. Hence, I am posting in AFL Programming category. (May we please have a category for Paid Solution Seekers also?)

Google search https://www.google.co.in/search?q=circle+through+three+points did return some simple mathematical formulas for the same.
The AFL portion to specify three points on the chart could be:

function GfxConvertBarToPixelX( bar )
{
    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    pxchartleft = Status( "pxchartleft" );
    pxchartwidth = Status( "pxchartwidth" );

    return pxchartleft + ( bar  * pxchartwidth / ( Lvb - fvb + 1 ) );
}

function GfxConvertValueToPixelY( Value )
{
    local Miny, Maxy, pxchartbottom, pxchartheight;

    Miny = Status( "axisminy" );
    Maxy = Status( "axismaxy" );

    pxchartbottom = Status( "pxchartbottom" );
    pxchartheight = Status( "pxchartheight" );

    return pxchartbottom - floor( 0.5 + ( Value - Miny ) * (pxchartheight / ( Maxy - Miny )) );
}

bi = BarIndex();

Point_A_X = beginValue( bi ) - FirstVisibleValue( bi );
Point_A_Y = beginValue( H );

Pixel_A_X = GfxConvertBarToPixelX( Point_A_X );
Pixel_A_Y = GfxConvertValueToPixelY( Point_A_Y );


Point_B_X = endValue( bi ) - FirstVisibleValue( bi );
Point_B_Y = endValue( H );
Pixel_B_X = GfxConvertBarToPixelX( Point_B_X );
Pixel_B_Y = GfxConvertValueToPixelY( Point_B_Y );


Point_C_X = SelectedValue( bi );
Point_C_Y = SelectedValue( H );
Pixel_C_X = GfxConvertBarToPixelX( Point_C_X );
Pixel_C_Y = GfxConvertValueToPixelY( Point_C_Y );

It will be fine if the solution uses GFX Pixel approach or Simple Bar/Value approach to find the Centre of the Circle. Thereafter the GFX function seem to have readimade functions for the circle.

I hope it is appropriate to post this request here. Will look forward to responses.

With Regards

Sanjiv Bansal

Instead of going to some rather overpriced incompetent snake oil sellers rather go to amibroker.com for AFL coding service.

Anyway... is the one below what you are looking for?
If so then let me know and I'll send full solution to your PM free of charge.
It's nothing special but just a few lines.

6 Likes

It was fun programming project for me, and nice example of how useful and powerful AmiBroker’s Matrix functions are, so here it is:


SetChartOptions( 0, chartHideQuoteMarker  );

function sqdist( x, y)
{
  return x * x + y * y;
}

p = StaticVarGet("pointsmx");

if( typeof( p ) != "matrix" )
{
  // initial matrix
  p = MxFromString( "{ { 100, 200 }, { 70, 50 }, { 40, 90 } }" );
}

CursorX = GetCursorXPosition(1);
CursorY = GetCursorYPosition(1);

but = GetCursorMouseButtons();

index = Nz( StaticVarGet( "index" ) );

if( but == 9 )
{
  p[ index ][ 0 ] = CursorX;
  p[ index ][ 1 ] = CursorY; 

  index++;
  index %= 3;
  
  StaticVarSet( "index", index );
  StaticVarSet( "pointsmx", p );
}

A = Matrix( 3, 3, 1 );
B = Matrix( 3, 1 );

for( i = 0; i < 3; i++ )
{
	A[ i ][ 0 ] = 2 * p[ i ][ 0 ];
	A[ i ][ 1 ] = 2 * p[ i ][ 1 ];
	B[ i ][ 0 ] = - sqdist( p[ i ][ 0 ], p[ i ][ 1 ] );
} 

X = MxSolve( A, B );

cx = - X[ 0 ][ 0 ];
cy = - X[ 1 ][ 0 ];
k  =   X[ 2 ][ 0 ];

r = sqrt( cy * cy + cx * cx - k );

GfxSelectStockObject( 5 );
GfxSelectPen( colorBlue );
GfxCircle( cx, cy, r );
GfxSetPixel( cx, cy, colorBlue );

for( i = 0; i < 3; i++ )
{
	x = p[ i ][ 0 ];
	y = p[ i ][ 1 ];

	GfxFillSolidRect( x - 1, y - 1 , x + 2, y + 2, colorYellow );
}

All the work is done by AmiBroker’s built-in MxSolve() that solves circle equation given 3 known points.
The example also shows that you can have entire matrices stored as static variables.

You can click on the chart to change the points. Each click changes co-ordinates of one point, so with first click you change x,y pos of first point, with second click you change 2nd point and so on in circular fashion.

Enjoy!

@Sumangalam - please give some $$$ to charity !

13 Likes

Hello Messrs. Tomasz, fxshrat and Kuba,

Thank you all very much.

Special Thanks to Mr. Tomasz for Building the eco-system of Software and Community where we can always be sure that solution and help will be certainly available at reasonable price (Which most of times happens to be free). And we can be sure that we will not be stuck.

To Mr. Fxshrat - for the elegant and slick solution - and taking time to create the Video - at The Most reasonable price (Free).

To Kuba - for the Cute solution - I presume you are very young - because young people are mostly pure JOY - for themselves and for their surroundings. May Almighty perennially maintain your Joyfulness.

Again, thank you all for the nice experience. I am looking forward to PM you all separately (After I get over with digesting the happiness).

Regards

Sanjiv Bansal

2 Likes

OK, since it is for charity (but don't send money to charity organization - it will just get lost. Rather give the $80+ to someone near you directly), here is non matrix version of picture of post #2 been sent to @Sumangalam PM box and being in line to the code in 1st post.

Basically you just need two functions...
One for perpenticular bisector's slope and another for perpenticular bisector's intercept.

function Gfx_PB_Slope( pointx1, pointy1, pointx2, pointy2 ) {
	// perpenticular bisector slope calculation
	slope = ( pointy2 - pointy1 ) / ( pointx2 - pointx1 + 1e-9 );
	rs = -1 / (slope + 1e-9);
	return rs;
}

function Gfx_PB_Intercept( pointx1, pointy1, pointx2, pointy2 ) {
	// perpenticular bisector intercept calculation	
	mid_X = ( pointx1 + pointx2 ) / 2;
	mid_Y = ( pointy1 + pointy2 ) / 2;
	rs = Gfx_PB_Slope( pointx1, pointy1, pointx2, pointy2 );
	rb = mid_Y - rs * mid_X;// intercept
	return rb;
}

Then the calculation for circle through three points is

pxAX = 100;
pxAY = 200;

pxBX = 300;
pxBY = 300;

pxCX = 300;
pxCY = 400;

// perpenticular bisector (A, B) calculation
rsAB = Gfx_PB_Slope( pxAX, pxAY, pxBX, pxBY );// slope
rbAB = Gfx_PB_Intercept( pxAX, pxAY, pxBX, pxBY );// intercept

// perpenticular bisector (B, C) calculations
rsBC = Gfx_PB_Slope( pxBX, pxBY, pxCX, pxCY );// slope
rbBC = Gfx_PB_Intercept(pxBX, pxBY, pxCX, pxCY);// intercept

// Circle's mid point (x,y) & radius calculations
circX = (rbBC - rbAB) / (rsAB - rsBC + 1e-9);
circY = rsBC * circX + rbBC;
rad = sqrt( (pxBX-circX)^2 + (pxBY-circY)^2 );

// draw circle through three points
GfxSelectPen( colorRed, 1, 0 );
GfxSelectStockObject( 5 ); // hollow circle
//GfxSelectSolidBrush( colorDarkRed ); // or filled circle
GfxCircle( circX, circY, rad );

Now, here is the full code using BeginValue(), EndValue(), SelectedValue() as in post#1.
Just for coding fun since I don't see any trading related value.
But maybe @Sumangalam will tell more about what he/she is after.

Usage:

  1. set some range via Range markers
  2. then use mouse to single click on some bar
// just for fun code of this AmiBroker forum thread
/// @link http://forum.amibroker.com/t/circle-through-three-points/1165/6

slvb  = Status( "lastvisiblebar" );
sfvb  = Status( "firstvisiblebar" );
Miny = Status( "axisminy" );
Maxy = Status( "axismaxy" );
pxchartleft = Status( "pxchartleft" );
pxchartwidth = Status( "pxchartwidth" );
pxchartbottom = Status( "pxchartbottom" );
pxchartheight = Status( "pxchartheight" );

function GfxBarToPixelX( bar ) {
    local bardiff, barnum, relbar, px;
    // based on http://www.amibroker.com/kb/2009/03/30/how-to-convert-from-bar-value-to-pixel-co-ordinates/
    barnum = slvb - sfvb + 1;
    bardiff = bar - sfvb;
	relbar = bardiff / barnum;
	px = relbar * pxchartwidth + pxchartleft;
    return Nz(px); 
}

function GfxPriceToPixelY( Value, logscale ) {
    local logMiny, logMinMax, pxy;
    //based on http://www.amibroker.com/kb/2009/03/30/how-to-convert-from-bar-value-to-pixel-co-ordinates/
    if ( logscale ) {
        logMiny = log( Miny );
        logMinMax = log( Maxy ) - logMiny;
        pxy = pxchartbottom - floor( 0.5 + ( log( Value ) - logMiny ) * pxchartheight / logMinMax );
    } else {
        pxy = pxchartbottom - floor( 0.5 + ( Value - Miny ) * pxchartheight / ( Maxy - Miny + 1e-9 ) );
    }
    return Nz(pxy);
}

function Gfx_PB_Slope( pointx1, pointy1, pointx2, pointy2 ) {
	// perpenticular bisector slope calculation
	slope = ( pointy2 - pointy1 ) / ( pointx2 - pointx1 + 1e-9 );
	rs = -1 / (slope + 1e-9);
	return rs;// slope
}

function Gfx_PB_Intercept( pointx1, pointy1, pointx2, pointy2 ) {
	// perpenticular bisector intercept calculation	
	mid_X = ( pointx1 + pointx2 ) / 2;
	mid_Y = ( pointy1 + pointy2 ) / 2;
	rs = Gfx_PB_Slope( pointx1, pointy1, pointx2, pointy2 );
	rb = mid_Y - rs * mid_X;// intercept
	return rb;
}

array = H;
bi = BarIndex();

_log = 0;

if( SelectedValue( bi ) < BeginValue( bi ) ) {
// if point B_X becomes smaller than point A_X
	Point_A_X = SelectedValue( bi );
	Point_A_Y = SelectedValue( array );	
	Point_B_X = BeginValue( bi );
	Point_B_Y = BeginValue( array );	
	Point_C_X = EndValue( bi );
	Point_C_Y = EndValue( array );	
} else if( SelectedValue( bi ) > EndValue( bi ) ) {
// if point B_X becomes larger than point C_X
	Point_A_X = BeginValue( bi );
	Point_A_Y = BeginValue( array );
	Point_B_X = EndValue( bi );
	Point_B_Y = EndValue( array );		
	Point_C_X = SelectedValue( bi );
	Point_C_Y = SelectedValue( array );
} else {
	Point_A_X = BeginValue( bi );
	Point_A_Y = BeginValue( array );
	Point_B_X = SelectedValue( bi );
	Point_B_Y = SelectedValue( array );
	Point_C_X = EndValue( bi );
	Point_C_Y = EndValue( array );
}

pxAX = GfxBarToPixelX( Point_A_X );
pxAY = GfxPriceToPixelY( Point_A_Y, _log );
pxBX = GfxBarToPixelX( Point_B_X );
pxBY = GfxPriceToPixelY( Point_B_Y, _log );
pxCX = GfxBarToPixelX( Point_C_X );
pxCY = GfxPriceToPixelY( Point_C_Y, _log );

// perpenticular bisector (A, B) calculation
midAB_X = (pxAX + pxBX) / 2;
midAB_Y = (pxAY + pxBY) / 2;
rsAB = Gfx_PB_Slope( pxAX, pxAY, pxBX, pxBY );// slope
rbAB = Gfx_PB_Intercept( pxAX, pxAY, pxBX, pxBY );// intercept

// perpenticular bisector (B, C) calculations
midBC_X = (pxBX + pxCX) / 2;
midBC_Y = (pxBY + pxCY) / 2;
rsBC = Gfx_PB_Slope( pxBX, pxBY, pxCX, pxCY );// slope
rbBC = Gfx_PB_Intercept(pxBX, pxBY, pxCX, pxCY);// intercept

// Circle's mid point & radius calculations
circX = (rbBC - rbAB) / (rsAB-rsBC + 1e-9);
circY = rsBC * circX + rbBC;
rad = sqrt( (pxBX-circX)^2 + (pxBY-circY)^2 );


// ########################################################################
// ###################### GFX DRAWING PART ################################
// ########################################################################

Plot( C, "Price", colorDefault, styleCandle );

GfxSetZOrder( 0 );
GfxSelectFont( "ARIAL", 12 );
GfxSetTextAlign( 6 | 0 );
GfxSetBkMode( 2 );

GfxSelectPen( colorRed, 1, 0 );
GfxSelectSolidBrush( colorDarkRed );

// Draw Line from A to B
GfxMoveTo( pxAX, pxAY );
GfxLineTo( pxBX, pxBY );

// Draw Line from B to C
GfxMoveTo( pxBX, pxBY );
GfxLineTo( pxCX, pxCY );

// draw points A, B, C
GfxCircle( pxAX, pxAY, 3 ); 
GfxCircle( pxBX, pxBY, 3 );
GfxCircle( pxCX, pxCY, 3 ); 

GfxTextOut( "A", pxAX, pxAY + 10 );
GfxTextOut( "B", pxBX, pxBY + 10 ); 
GfxTextOut( "C", pxCX, pxCY + 10 );

GfxSelectPen( colorGold, 1, 0 );

// Draw perpenticular bisector lines
GfxMoveTo( midAB_X, midAB_Y );
GfxLineTo( circX, circY );

GfxMoveTo( midBC_X, midBC_Y );
GfxLineTo( circX, circY );

// AB mid point
GfxCircle( midAB_X, midAB_Y, 3 );
// BC mid point
GfxCircle( midBC_X, midBC_Y, 3 );

// draw circle's mid point
GfxCircle( circX, circY, 3 ); 

// draw circle through three points
GfxSetZOrder( -1 );
GfxSelectPen( colorRed, 1, 0 );
GfxSelectStockObject( 5 ); // hollow circle
//GfxSelectSolidBrush( colorDarkRed ); // or filled circle
GfxCircle( circX, circY, rad );

10 Likes

Hello Fxshrat,

Thank you again for the elegant code.

Actually, an AmiBroker user friend had asked for some coding, and this Circle was part of that. There is some payment to be made to me for that, that is why I had seeked this AFL on payment basis (If I get money, I should pay - Simple).

Now, that the Forum has provided this code with the understanding of Contribution to some worthy cause - I have forwarded same understanding for this part of code to the friend (Simple and Fair - Right?).

You are right - I am also feeling that this Circle thing may not have much of Trading Value - but he should know best. However, your and Kuba’s code has great learning value.

After the Google search mentioned in Post#1, (Honestly, as mentioned by Mr. Tomasz elsewhere - I had scanned the Google results and when it came to understanding the solutions suggested - I had intentionally stopped myself). I realised that understanding these things will need me to go back to school (for a few hours/days). It has been more than three decades since leaving school. You and Kuba have done great favour, my teachers do not have to bear with me again.

@fxshrat, your implementation of GfxPriceToPixelY seems to be more powerful that the example in Helpfile - It has support for LogScale also. I hope @Tomasz implements Status(“AxesTypeY”) - so that the AFL can know if Linear or Logarithmic scale is to be used. Further, you might have observed that GraphXSpace also affects GfxPriceToPixelY, I will experiment with these things over the weekend and get back.

Thank you all again for the nice implementations and gestures.

Regards

Sanjiv Bansal

3 Likes