Line cap calculation
To continue my previous post, I would like to explain a little more about how the line cap is calculated.
Here is the order at which the line is rendered:
1. Line Start Cap calculation
2. Join vertex calculation n1
3. ...
4. Join vertex calculation n1 + joinNum
5. Line End Cap calculation
As you can see the cap starts the line and ends it. Because I am using GL_QUAD_STRIP then it is important to properly connect all the vertices together, and espcially make their total number base power of 4, because each quad consists of 4 points.
There are usually 3 different cap styles:
1. None (the cap vertexes are created at origin of the start/end point) [2 vertexes]
2. Square (the cap vertexes are created at the same origin but with an offset equal the the line width)[2 vertexes]
3. Round (the cap vertexes form half-arc at the origin of the start/end point)[2 + 2*iterations vertexes]
So The first thing that is required - the general cap function:
//Calculates cap for the line
void CalculateCap( LineVertex v0,
LineVertex v1,
bool start ) //decides is this cap starts the line = true or ends = false
{
float len = CalculateDistance( v0 , v1 );
float dx1 = (v1.posY - v0.posY) / len;
float dy1 = (v1.posX - v0.posX) / len;
float dx2 = 0;
float dy2 = 0;
dx1 *= _size;
dy1 *= _size;
if( _capStyle != CapStyle_Round)
{
if( _capStyle == CapStyle_Square)
{
dx2 = dy1 ;
dy2 = dx1 ;
}
if( start )
{
AddVertex( v0.posX + dx1 - dx2, v0.posY - dy1 - dy2);
AddVertex( v0.posX - dx1 - dx2, v0.posY + dy1 - dy2);
}
else
{
//end of path
AddVertex( v0.posX - dx1 - dx2, v0.posY + dy1 - dy2);
AddVertex( v0.posX + dx1 - dx2, v0.posY - dy1 - dy2);
}
}
else
{
//Converts radian result of atan2 to Deg we need for comaring later
float subAngle = -(TRIG_RAD2DEG( atan2( dx1 , dy1 ) ) - 180);
//simple but does check the direction of the line edge
if( (subAngle > 90 )&&( subAngle <= 270 ) )
{
if( start )
{
CalculateRound( v0 ,
dx1, -dy1 ,
-dx1, dy1 ,
v0.posX + dx1 ,
v0.posY - dy1 ,
true );
}
else
{
CalculateRound( v0 ,
dx1, -dy1 ,
-dx1, dy1 ,
v0.posX - dx1 ,
v0.posY + dy1 ,
true );
}
}
else
{
if( start )
{
CalculateRound( v0 ,
-dx1, dy1 ,
dx1, -dy1 ,
v0.posX - dx1 ,
v0.posY + dy1 ,
false );
}
else
{
CalculateRound( v0 ,
-dx1, dy1 ,
dx1, -dy1 ,
v0.posX + dx1 ,
v0.posY - dy1 ,
false );
}
}
}
}
This general function finds the result vertexes this way:
1. Finds relative position of the initial vertexes. Exactly the same way as it was made with "None" line join.
2.Now depending on the Cap style:
None - Just use these calculated vertexes and add them to the render buffer.
Square - Apply line width multiplier and add them to the render buffer.
Round - This is more complicated:
For round join the renderer needs to calculate arc:
void CalculateRound(
LineVertex v0, //center of the line join
float dx1, float dy1, //the start point
float dx2, float dy2, //the end point
float middleX ,
float middleY , //The middle point for creating correct GL_QUAD vertex bool reverse) //use reverse incase of inner join
{
float a1 = TRIG_RAD2DEG( atan2( dy1 , dx1 ));
float a2 = TRIG_RAD2DEG( atan2( dy2 , dx2 ));
float angleStep = (a1 - a2) ; //number of iterations
angleStep = angleStep / (iterations + 1) ; //starting angle
float cangle = 0;
if( reverse )
{
//INNER JOIN
AddVertex( middleX , middleY );
AddVertex( v0.posX - dx2 , v0.posY - dy2);
a2 += 180;
cangle = a2 ;
for(int i = 0; i < iterations ; i++)
{
cangle += angleStep; //Also put the middle point for the GL to correctly link other GL_QUAD later
AddVertex( middleX , middleY ); //find the rotated point AddVertex(
_size * trig.Cos(cangle) + v0.posX ,
_size * trig.Sin(cangle) + v0.posY );
}
AddVertex( middleX , middleY );
AddVertex( v0.posX - dx1, v0.posY - dy1);
}
else
{
//OUTER JOIN
AddVertex( v0.posX + dx1 , v0.posY + dy1);
AddVertex( middleX , middleY ); //change the direction of angle iteration
angleStep *= -1;
cangle = a1 ;
for(int i = 0; i < iterations ; i++)
{
cangle += angleStep; //find the rotated point
AddVertex(
_size * trig.Cos(cangle) + v0.posX ,
_size * trig.Sin(cangle) + v0.posY ); //Also put the middle point for the GL to correctly link other GL_QUAD later
AddVertex( middleX , middleY );
}
AddVertex( v0.posX + dx2, v0.posY + dy2);
AddVertex( middleX , middleY );
}
}
The arc calculation is not that hard. For this we need to have the center point, the start and end point. The middleX and middleY are used as an additional point to connect quad with.
We also need to find the start angle from where to start iteration and the end angle when to end.
Each iterations adds additional 2 vertexes. If you look closely for the iteration code, you would see that it generates minimum 4 vertexes. The first 2 vertexes are actually at the same point, they start the cap, then there are iteration vertexes and at the end 2 more for opening the next 2 vertexes
The order of drawing for 3 iterations is shown next. This is for the start cap with values:
CalculateRound( v0 ,
dx1, -dy1 ,
-dx1, dy1 ,
v0.posX + dx1 ,
v0.posY - dy1 ,
true );
Initial state:
1. First 2 vertexes and iteration 0
2. Iteration 1
3. Iteration 2
4. And now the end 2 vertexes are calculated
This is probably all that is required for rendering line cap styles.
























1 Comments:
hi~nice blog, love your site~visit again,c ya
Post a Comment
Links to this post:
Create a Link
<< Home