Requires Eigen as written, but the core operations should map easily to whatever vector class you’re using.
// v0 and v1 are normalized
// t can vary between 0 and 1
// http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
Vector2f slerp2d( const Vector2f& v0, const Vector2f& v1, float t )
{
float dot = v0.dot(v1);
if( dot < -1.0f ) dot = -1.0f;
if( dot > 1.0f ) dot = 1.0f;
float theta_0 = acos( dot );
float theta = theta_0 * t;
Vector2f v2( -v0.y(), v0.x() );
return ( v0*cos(theta) + v2*sin(theta) );
}
void glPolyline( const vector<Vector2f>& polyline, float width )
{
if( polyline.size() < 2 ) return;
float w = width / 2.0f;
glBegin(GL_TRIANGLES);
for( size_t i = 0; i < polyline.size()-1; ++i )
{
const Vector2f& cur = polyline[ i ];
const Vector2f& nxt = polyline[i+1];
Vector2f b = (nxt - cur).normalized();
Vector2f b_perp( -b.y(), b.x() );
Vector2f p0( cur + b_perp*w );
Vector2f p1( cur - b_perp*w );
Vector2f p2( nxt + b_perp*w );
Vector2f p3( nxt - b_perp*w );
// first triangle
glVertex2fv( p0.data() );
glVertex2fv( p1.data() );
glVertex2fv( p2.data() );
// second triangle
glVertex2fv( p2.data() );
glVertex2fv( p1.data() );
glVertex2fv( p3.data() );
// only do joins when we have a prv
if( i == 0 ) continue;
const Vector2f& prv = polyline[i-1];
Vector2f a = (prv - cur).normalized();
Vector2f a_perp( a.y(), -a.x() );
float det = a.x()*b.y() - b.x()*a.y();
if( det > 0 )
{
a_perp = -a_perp;
b_perp = -b_perp;
}
// TODO: do inner miter calculation
// flip around normals and calculate round join points
a_perp = -a_perp;
b_perp = -b_perp;
size_t num_pts = 4;
vector< Vector2f > round( 1 + num_pts + 1 );
for( size_t j = 0; j <= num_pts+1; ++j )
{
float t = (float)j/(float)(num_pts+1);
if( det > 0 )
round[j] = cur + (slerp2d( b_perp, a_perp, 1.0f-t ) * w);
else
round[j] = cur + (slerp2d( a_perp, b_perp, t ) * w);
}
for( size_t j = 0; j < round.size()-1; ++j )
{
glVertex2fv( cur.data() );
if( det > 0 )
{
glVertex2fv( round[j+1].data() );
glVertex2fv( round[j+0].data() );
}
else
{
glVertex2fv( round[j+0].data() );
glVertex2fv( round[j+1].data() );
}
}
}
glEnd();
}
EDIT: Screenshots: