Advertisement

Elliptical arc length

Started by January 30, 2019 07:01 AM
7 comments, last by JB3DG 6ย years ago

Hi guys

I got a bit of a challenge here. The shader shown below puts out a small quad for each of the 90 vertices sent to it to generate an arc line. I plan on supporting dash patterns based on line length in the pixel shader later, which means I need to place line length in the texture coordinates in the geometry shader. The problem is, this arc isn't always going to be perfectly circular. It can become compressed by the bounding rectangle into an ellipse. And so far my google searches have indicated that finding the length of an elliptical arc is no small task.

Anyone got any ideas?


[maxvertexcount(80)]
void GS_DRAWARC(point ARC_INPUTG arc[1], inout TriangleStream<PS_INPUT> triStream)
{
	PS_INPUT v;
	int n = 90;
	float w1 = inset == 0 ? (width.x*0.5f) : 0.0f;
	float w2 = inset == 0 ? -(width.x*0.5f) : -width.x;

	float xr = arc[0].dim.z * 0.5f;
	float yr = arc[0].dim.w * 0.5f;

	float2 center = float2(arc[0].dim.x + xr, arc[0].dim.y + yr);

	int inst = floor(arc[0].id / darray.y);
	int id = arc[0].id % darray.y;

	if (darray.x > 0)
	{
		if (darray.x == 1)
			center.xy += float2(darraymod.xy*float(inst));
		if (darray.x == 2)
			center.xy += float2(fmod(darraymod.x*float(inst), darraymod.z), fmod(darraymod.y*float(inst), darraymod.z));
	}
	
	for (int i = 0; i <= 1; i++)
	{
		int stop = 0;
		float end = arc[0].arc.x + arc[0].arc.y;
		int j = id + i;
		float a = ((float)j / n * 2.0f * 3.1415926f) * (end < 0 ? -1.0f : 1.0f);
		if (abs(a) > abs(end))
		{
			a = end;
			stop = 1;
		}

		float s, c;
		sincos(a, s, c);

		float r = 1.0f / sqrt(pow(c / xr, 2.0f) + pow(s / yr, 2.0f));
		float r2 = r + (end < 0 ? w1 : w2);
		float r1 = r + (end < 0 ? w2 : w1);

		float _s, _c;
		sincos(darraymod.w*float(inst), _s, _c);
		float2x2 rot = { _c, -_s, _s, _c };

		float2 _p = float2(r2*s, r2*-c) + center;
		if (darray.x == 3)
			_p = mul(_p, rot);
		float4 p = mul(float4(_p*dscale, 0, 1.0f), dxform);
		v.p = p*float4(dnorm*2.0f, 1.0f, 1.0f);
		v.p.x -= 1.0f;
		v.p.y = -v.p.y + 1.0f;
		v.t = float2(0.5*s, 0.5*c) + float2(0.5f, 0.5f);
		triStream.Append(v);
		
		_p = float2(r1*s, r1*-c) + center;
		if (darray.x == 3)
			_p = mul(_p, rot);
		p = mul(float4(_p*dscale, 0, 1.0f), dxform);
		v.p = p*float4(dnorm*2.0f, 1.0f, 1.0f);
		v.p.x -= 1.0f;
		v.p.y = -v.p.y + 1.0f;
		v.t = float2(0.5*s, 0.5*c) + float2(0.5f, 0.5f);
		triStream.Append(v);
		if (stop != 0)
		{
			triStream.RestartStrip();
			break;
		}
	}
}

ย 

Could you add all the segments/edges (curve fragments?) together to get a 'good enough' approximation?

๐Ÿ™‚๐Ÿ™‚๐Ÿ™‚๐Ÿ™‚๐Ÿ™‚<โ†The tone posse, ready for action.

Advertisement

I was about to mention just that, since apparently a Ngon is expressed differently mathematically to a pure ellipse.

However, I don't have access to previous segments to add up their lengths, and I am trying to keep my rendering code as efficient and fast as possible, so I am reluctant to resort to additional for loops to add them up.

The Ngon idea does seem to be a step in the right direction, since my 2D library also has what I call "DrawNGon" functions which are the same as the draw ellipse function, but allow to specify the number of sides, anywhere from 3 to X, and I need to be able to get the perimeter length from the starting point to any end point around the shape.

On second thoughts, if there is a way to do a loop in a shader that is spread out over multiple GPU threads so I can calculate the length of each segment and add them all together in parallel, I might be able to do this.

You are drawing your ellipse in a relatively cheap way, as a stretched circle, parametrized by angle to the center. This stretches your segments: if you want equidistant points along the ellipse there are other (more complex) approaches.

For example, you can use iterative methods to approximate (possibly very well, depending on budget, but a decent result should be cheap) the intersection between the ellipse and the circle of a suitable fixed small radius centered on the previous vertex, obtaining a sequence of points that have the same distance from each other and lie on the ellipse. Stopping at the desired endpoint requires additional tests and clipping.

Omae Wa Mou Shindeiru

Would this method for finding equidistant points around an ellipse help?

ย https://math.stackexchange.com/questions/172766/calculating-equidistant-points-around-an-ellipse-arc

Advertisement
1 hour ago, Irusan, son of Arusan said:

Would this method for finding equidistant points around an ellipse help?

ย https://math.stackexchange.com/questions/172766/calculating-equidistant-points-around-an-ellipse-arc

Straightforward but expensive, given the need for many evaluations of elliptic integrals (at least one per vertex).

Omae Wa Mou Shindeiru

Since I am using 90 vertices for a complete 360 deg arc, I have settled with adding up the lengths based on the number of vertices before the vertex being processed in the geometry shader. It does the job for now but hopefully a more efficient solution can be found.

This topic is closed to new replies.

Advertisement