Draw Circle

Started by Vincent, Sat 14/11/2020 17:02:44

Previous topic - Next topic

Vincent

Good evening to all Agser.

I am having some difficulties doing something that I hope some of you can help.
I'm trying to draw a circle which for every second his arc get erased by a small portion until the arc is completely erased. How do you do such a thing?
Any kind of help is appreciated, thanks in advance.

eri0o

I sort don't have much time now, but the way I would do is create a sprite with transparency of the size of the circle, draw a circle with it's color, then use transparency color to   draw a triangle  with one of it's points in the circle center. You will need one triangle per circle quarter.

Vincent

Thanks eri0o for your input, somehow I am afraid that I still dont know how to make what you are explaining, I hope you will find some time later.

Crimson Wizard

#3
So, we need to add TODO: DrawArc function.  :smiley:

Quote from: Vincent on Sat 14/11/2020 18:23:10
Thanks eri0o for your input, somehow I am afraid that I still dont know how to make what you are explaining, I hope you will find some time later.

If there were a DrawArc function, then you could use that, but since there is not, you may use DrawTriangle instead, having one of the triangle's corner in the circle's center.
The point is in drawing a triangle of "erase" color, whatever that is for your case (transparent, or background color, if you have a plain background).
You calculate triangle corners so that they create a sort of arc.


Assuming you know circle's radius, triangle's point 1 is at circle's center, as mentioned, point 2 and point 3 are on the circle's outer border (aka circumference). So it goes down solving math question:
https://math.stackexchange.com/questions/260096/find-the-coordinates-of-a-point-on-a-circle
You may take larger radius just in case, to make sure triangle covers all necessary circle's pixels.

EDIT: something like following, unless I missed anything:
Code: ags

int x1 = circle_center_x;
int y1 = circle_center_y;
int x2 = circle_center_x + circle_radius * Maths.Sin(arc_angle1);
int y2 = circle_center_y + circle_radius * Maths.Cos(arc_angle1);
int x3 = circle_center_x + circle_radius * Maths.Sin(arc_angle2);
int y3 = circle_center_y + circle_radius * Maths.Cos(arc_angle2);

ds.DrawTriangle(x1, y1, x2, y2, x3, y3);


arc_angle1 and arc_angle2 define which sector of the circle you want to erase.

Vincent

I have this small piece of code to get started:

Code: ags

DrawingSurface *surface;
DynamicSprite *screen;

function room_Load()
{
  screen = DynamicSprite.Create(640, 480);
  surface = screen.GetDrawingSurface();

  surface.DrawingColor = 1055;
  surface.DrawCircle(250, 200, 50);
  gScreen.BackgroundGraphic = screen.Graphic;
}


I think the initial circle should get drawn only once in room load.
Now the arc should get erased by a very small portion, let's say 1 pixel for every second, until the arc is completely erased.
How it should erase the arc repeatedly using your formula? The code is also giving me some errors which it can't convert int to float but this is a minor issue.

Crimson Wizard

#5
Quote from: Vincent on Sun 15/11/2020 09:35:17
Now the arc should get erased by a very small portion, let's say 1 pixel for every second, until the arc is completely erased.

First set arc_angle1 to 0 and arc_angle2 to some slightly larger value N (you will have to experiment here, because it will also depend on circle radius to look nice). These values may be in radians for simplier use with Sin and Cos functions. You may also use degrees, but then you have to convert them to radians before calling Sin and Cos (using Maths.DegreesToRadians).
I think all these variables, including circle_center_x/y circle_radius, etc, should be floats for more accurate computations, and only converted to int when assigned to triangle parameters.

Then, I guess, you start a timer, and every time timer runs out, you draw a triangle with above code, and then increase both of the arc angle variables (arc_angle1 and arc_angle2) by same value N equal to their difference, until arc_angle2 reaches 2 Pi (6.28 roughly).

I see you want to draw this on a separate sprite, but idk if anything else is drawn there or how it is used later. So depending on that you draw these triangles with transparent color, or some "background color".



UPDATE

Ok, I made a quick test, and following room script seem work well:
Spoiler

Code: ags

DynamicSprite *screen;

float arc_angle1, arc_angle2;
float arc_size;
float arc_start, arc_end;
float circle_center_x, circle_center_y;
float circle_radius;

int timer_time;

function room_Load()
{
	screen = DynamicSprite.Create(gScreen.Width, gScreen.Height);
	DrawingSurface* surface = screen.GetDrawingSurface();
 
	circle_center_x = 150.0;
	circle_center_y = 100.0;
	circle_radius = 50.0 + 10.0;
	arc_start = Maths.Pi / 2.0;
	arc_end = Maths.Pi / 2.0 + Maths.Pi * 2.0;
	arc_size = 0.1;
	arc_angle1 = arc_start;
	arc_angle2 = arc_angle1 + arc_size;
 
	surface.DrawingColor = 1055;
	surface.DrawCircle(150, 100, 50);
	gScreen.BackgroundGraphic = screen.Graphic;
	surface.Release();
	
	timer_time = 5;
}

function room_AfterFadeIn()
{
	SetTimer(1, timer_time);
}

function room_RepExec()
{
	if (IsTimerExpired(1))
	{
		int x1 = FloatToInt(circle_center_x, eRoundUp);
		int y1 = FloatToInt(circle_center_y, eRoundUp);
		int x2 = FloatToInt(circle_center_x + circle_radius * Maths.Sin(arc_angle1), eRoundUp);
		int y2 = FloatToInt(circle_center_y + circle_radius * Maths.Cos(arc_angle1), eRoundUp);
		int x3 = FloatToInt(circle_center_x + circle_radius * Maths.Sin(arc_angle2), eRoundUp);
		int y3 = FloatToInt(circle_center_y + circle_radius * Maths.Cos(arc_angle2), eRoundUp);
		
		DrawingSurface* ds = screen.GetDrawingSurface();
		ds.DrawingColor = COLOR_TRANSPARENT;
		ds.DrawTriangle(x1, y1, x2, y2, x3, y3);
		ds.Release();
		
		arc_angle1 += arc_size;
		arc_angle2 += arc_size;
		if (arc_angle1 < arc_end)
		{
			SetTimer(1, timer_time);
		}
	}
}

[close]

Vincent

I think this was a way a bit too complicated to script by myself. Your quick test its working perfect as I intended, thanks for your help CW.



One last thing how do I start the erasion of the arc starting from above instead of starting it from the middle as you did?

Crimson Wizard

#7
arc_start variable is set to where the erasion begins. Maybe I did some math mistake, because setting it to 0 makes it start at the bottom (I was expecting 0 to start at the right side).
Anyways, if 0 is bottom and Pi/2 is right, then arc_start = Maths.Pi should be top. (Pi/2 is one quarter of a circle, Pi makes half-circle, 2*Pi full circle)

EDIT: Ah, of course, I know what I did wrong. The Sin should be used for Y coord, and Cos for X coord:
Code: ags

int x2 = FloatToInt(circle_center_x + circle_radius * Maths.Cos(arc_angle1), eRoundUp);
int y2 = FloatToInt(circle_center_y + circle_radius * Maths.Sin(arc_angle1), eRoundUp);
int x3 = FloatToInt(circle_center_x + circle_radius * Maths.Cos(arc_angle2), eRoundUp);
int y3 = FloatToInt(circle_center_y + circle_radius * Maths.Sin(arc_angle2), eRoundUp);


With this arc_start = 0 make it begin at the right side, arc_start = Maths.Pi / 2.0 will begin at the top.

But then you also need to subtract arc_size to make it go counter-clockwise, because Y axis is inverted in AGS (points down).
during initialization:
Code: ags

arc_angle1 = arc_start;
arc_angle2 = arc_angle1 - arc_size;

and in rep-exec:
Code: ags

arc_angle1 -= arc_size;
arc_angle2 -= arc_size;

Vincent

Ok so doing your last changes it start the erasion from the bottom now, but using your scripts from the quick test and changing only the arc_start and arc_end to be:
Code: ags

arc_start = Maths.Pi;
arc_end = Maths.Pi + Maths.Pi * 2.0;


It start the erasion at the top as I intended.


Crimson Wizard

Quote from: Vincent on Sun 15/11/2020 12:10:55
Ok so doing your last changes it start the erasion from the bottom now

With these last changes arc_start should be arc_start = -Maths.Pi / 2.0;, because the Y axis is inverted in AGS.

Vincent

It seems to be working all good with your last changes.
With your oldest script I was able to run an addictional line of code which it display a message:

Code: ags

if (arc_angle1 < arc_end) SetTimer(1, timer_time);
else Display("time out");


But now isn't working anymore, it seems that the timer is called endlessly.

Crimson Wizard

Quote from: Vincent on Sun 15/11/2020 12:55:56
Code: ags

if (arc_angle1 < arc_end) SetTimer(1, timer_time);
else Display("time out");


But now isn't working anymore, it seems that the timer is called endlessly.

If you are subtracting arc_size, the arc_end needs also to be adjusted to arc_end = Maths.Pi / 2.0 - Maths.Pi * 2.0; and < sign should be replaced with > in the final condition.

Vincent

#12
Thanks for taking the time on helping with this I appreciate it a lot.

EDIT: Well by doing so the message appear when it reach the bottom, I dont know what I am missing.

Crimson Wizard

#13
If you do arc_end = arc_start - Maths.Pi * 2.0; then it should work with any arc_start, without a need to adjust it if you change its value.

This is the code I have in the end
Spoiler

Code: ags

DynamicSprite *screen;

float arc_angle1, arc_angle2;
float arc_size;
float arc_start, arc_end;
float circle_center_x, circle_center_y;
float circle_radius;

int timer_time;

function room_Load()
{
	screen = DynamicSprite.Create(gScreen.Width, gScreen.Height);
	DrawingSurface* surface = screen.GetDrawingSurface();
 
	circle_center_x = 150.0;
	circle_center_y = 100.0;
	circle_radius = 50.0 + 10.0;
	arc_start = -Maths.Pi / 2.0;
	arc_end = arc_start - Maths.Pi * 2.0;
	arc_size = 0.03;
	arc_angle1 = arc_start;
	arc_angle2 = arc_angle1 - arc_size;
 
	surface.DrawingColor = 1055;
	surface.DrawCircle(150, 100, 50);
	gScreen.BackgroundGraphic = screen.Graphic;
	surface.Release();
	
	timer_time = 2;
}

function room_AfterFadeIn()
{
	SetTimer(1, timer_time);
}

function room_RepExec()
{
	if (IsTimerExpired(1))
	{
		int x1 = FloatToInt(circle_center_x, eRoundUp);
		int y1 = FloatToInt(circle_center_y, eRoundUp);
		int x2 = FloatToInt(circle_center_x + circle_radius * Maths.Cos(arc_angle1), eRoundUp);
		int y2 = FloatToInt(circle_center_y + circle_radius * Maths.Sin(arc_angle1), eRoundUp);
		int x3 = FloatToInt(circle_center_x + circle_radius * Maths.Cos(arc_angle2), eRoundUp);
		int y3 = FloatToInt(circle_center_y + circle_radius * Maths.Sin(arc_angle2), eRoundUp);
		
		DrawingSurface* ds = screen.GetDrawingSurface();
		ds.DrawingColor = COLOR_TRANSPARENT;
		ds.DrawTriangle(x1, y1, x2, y2, x3, y3);
		ds.Release();
		
		arc_angle1 -= arc_size;
		arc_angle2 -= arc_size;
		if (arc_angle1 > arc_end)
		{
			SetTimer(1, timer_time);
		}
		else
		{
			Display("time out");
		}
	}
}


[close]

Vincent

#14
Alright doing:

Code: ags
arc_end = arc_start - Maths.Pi * 2.0;


I don't need to do any further changes, it's all working good this time, thanks a lot again.

Quote from: Crimson Wizard on Sun 15/11/2020 13:53:00
Spoiler

Code: ags

DynamicSprite *screen;

float arc_angle1, arc_angle2;
float arc_size;
float arc_start, arc_end;
float circle_center_x, circle_center_y;
float circle_radius;

int timer_time;

function room_Load()
{
	screen = DynamicSprite.Create(gScreen.Width, gScreen.Height);
	DrawingSurface* surface = screen.GetDrawingSurface();
 
	circle_center_x = 150.0;
	circle_center_y = 100.0;
	circle_radius = 50.0 + 10.0;
	arc_start = -Maths.Pi / 2.0;
	arc_end = arc_start - Maths.Pi * 2.0;
	arc_size = 0.03;
	arc_angle1 = arc_start;
	arc_angle2 = arc_angle1 - arc_size;
 
	surface.DrawingColor = 1055;
	surface.DrawCircle(150, 100, 50);
	gScreen.BackgroundGraphic = screen.Graphic;
	surface.Release();
	
	timer_time = 2;
}

function room_AfterFadeIn()
{
	SetTimer(1, timer_time);
}

function room_RepExec()
{
	if (IsTimerExpired(1))
	{
		int x1 = FloatToInt(circle_center_x, eRoundUp);
		int y1 = FloatToInt(circle_center_y, eRoundUp);
		int x2 = FloatToInt(circle_center_x + circle_radius * Maths.Cos(arc_angle1), eRoundUp);
		int y2 = FloatToInt(circle_center_y + circle_radius * Maths.Sin(arc_angle1), eRoundUp);
		int x3 = FloatToInt(circle_center_x + circle_radius * Maths.Cos(arc_angle2), eRoundUp);
		int y3 = FloatToInt(circle_center_y + circle_radius * Maths.Sin(arc_angle2), eRoundUp);
		
		DrawingSurface* ds = screen.GetDrawingSurface();
		ds.DrawingColor = COLOR_TRANSPARENT;
		ds.DrawTriangle(x1, y1, x2, y2, x3, y3);
		ds.Release();
		
		arc_angle1 -= arc_size;
		arc_angle2 -= arc_size;
		if (arc_angle1 > arc_end)
		{
			SetTimer(1, timer_time);
		}
		else
		{
			Display("time out");
		}
	}
}

[close]

Yes, this is how I have it right now and it's working all good! (nod)

SMF spam blocked by CleanTalk