我使用以下算法来生成多边形轮廓:
void OGLSHAPE::GenerateLinePoly(std::vector<DOUBLEPOINT> &input, int width)
{
OutlineVec.clear();
if(input.size() < 2)
{
return;
}
if(connected)
{
input.push_back(input[0]);
input.push_back(input[1]);
}
float w = width / 2.0f;
//glBegin(GL_TRIANGLES);
for( size_t i = 0; i < input.size()-1; ++i )
{
POINTFLOAT cur;
cur.x = input[i].point[0];
cur.y = input[i].point[1];
POINTFLOAT nxt;
nxt.x = input[i+1].point[0];
nxt.y = input[i+1].point[1];
POINTFLOAT b;
b.x = nxt.x - cur.x;
b.y = nxt.y - cur.y;
b = normalize(b);
POINTFLOAT b_perp;
b_perp.x = -b.y;
b_perp.y = b.x;
POINTFLOAT p0;
POINTFLOAT p1;
POINTFLOAT p2;
POINTFLOAT p3;
p0.x = cur.x + b_perp.x * w;
p0.y = cur.y + b_perp.y * w;
p1.x = cur.x - b_perp.x * w;
p1.y = cur.y - b_perp.y * w;
p2.x = nxt.x + b_perp.x * w;
p2.y = nxt.y + b_perp.y * w;
p3.x = nxt.x - b_perp.x * w;
p3.y = nxt.y - b_perp.y * w;
OutlineVec.push_back(p0.x);
OutlineVec.push_back(p0.y);
OutlineVec.push_back(p1.x);
OutlineVec.push_back(p1.y);
OutlineVec.push_back(p2.x);
OutlineVec.push_back(p2.y);
OutlineVec.push_back(p2.x);
OutlineVec.push_back(p2.y);
OutlineVec.push_back(p1.x);
OutlineVec.push_back(p1.y);
OutlineVec.push_back(p3.x);
OutlineVec.push_back(p3.y);
// only do joins when we have a prv
if( i == 0 ) continue;
POINTFLOAT prv;
prv.x = input[i-1].point[0];
prv.y = input[i-1].point[1];
POINTFLOAT a;
a.x = prv.x - cur.x;
a.y = prv.y - cur.y;
a = normalize(a);
POINTFLOAT a_perp;
a_perp.x = a.y;
a_perp.y = -a.x;
float det = a.x * b.y - b.x * a.y;
if( det > 0 )
{
a_perp.x = -a_perp.x;
a_perp.y = -a_perp.y;
b_perp.x = -b_perp.x;
b_perp.y = -b_perp.y;
}
// TODO: do inner miter calculation
// flip around normals and calculate round join points
a_perp.x = -a_perp.x;
a_perp.y = -a_perp.y;
b_perp.x = -b_perp.x;
b_perp.y = -b_perp.y;
size_t num_pts = 4;
std::vector< POINTFLOAT> round( 1 + num_pts + 1 );
POINTFLOAT nc;
nc.x = cur.x + (a_perp.x * w);
nc.y = cur.y + (a_perp.y * w);
round.front() = nc;
nc.x = cur.x + (b_perp.x * w);
nc.y = cur.y + (b_perp.y * w);
round.back() = nc;
for( size_t j = 1; j < num_pts+1; ++j )
{
float t = (float)j/(float)(num_pts+1);
if( det > 0 )
{
POINTFLOAT nin;
nin = slerp2d( b_perp, a_perp, 1.0f-t );
nin.x *= w;
nin.y *= w;
nin.x += cur.x;
nin.y += cur.y;
round[j] = nin;
}
else
{
POINTFLOAT nin;
nin = slerp2d( a_perp, b_perp, t );
nin.x *= w;
nin.y *= w;
nin.x += cur.x;
nin.y += cur.y;
round[j] = nin;
}
}
for( size_t j = 0; j < round.size()-1; ++j )
{
OutlineVec.push_back(cur.x);
OutlineVec.push_back(cur.y);
if( det > 0 )
{
OutlineVec.push_back(round[j + 1].x);
OutlineVec.push_back(round[j + 1].y);
OutlineVec.push_back(round[j].x);
OutlineVec.push_back(round[j].y);
}
else
{
OutlineVec.push_back(round[j].x);
OutlineVec.push_back(round[j].y);
OutlineVec.push_back(round[j + 1].x);
OutlineVec.push_back(round[j + 1].y);
}
}
}
}
POINTFLOAT multiply(const POINTFLOAT &a, float b)
{
POINTFLOAT result;
result.x = a.x * b;
result.y = a.y * b;
return result;
}
POINTFLOAT normalize(const POINTFLOAT &a)
{
return multiply(a, 1.0f/sqrt(a.x*a.x+a.y*a.y));
}
POINTFLOAT slerp2d( const POINTFLOAT &v0,
const POINTFLOAT &v1, float t )
{
float dot = (v0.x * v1.x + v0.y * v1.y);
if( dot < -1.0f ) dot = -1.0f;
if( dot > 1.0f ) dot = 1.0f;
float theta_0 = acos( dot );
float theta = theta_0 * t;
POINTFLOAT v2;
v2.x = -v0.y;
v2.y = v0.x;
POINTFLOAT result;
result.x = v0.x * cos(theta) + v2.x * sin(theta);
result.y = v0.y * cos(theta) + v2.y * sin(theta);
return result;
}
我注意到 vector 绘图应用程序允许四舍五入一段的开始和结束。如何修改行生成算法以舍入未连接段的开始和结束?
请参阅以下示例,以了解我的意思:
alt text http://img39.imageshack.us/img39/6029/capss.png
谢谢
最佳答案
花了我一段时间来了解slerp2d()的工作原理,我仍然可能会出错,但是令我惊讶的是,您可以使用单位 vector ,并且它垂直于两端,并用它们绘制半球的两半。
只要两端不符合要求,请使用slerp2d(-b,b_perp,t);和slerp2d(-b,-b_perp,t);首先使用slerp2d(b,b_perp,t)(条款的顺序可能需要交换);和slerp2d(b,-b_perp,t);最后。
您可以避免再次计算round.back(),因为它仍然是P0(或P1,取决于确定的),而round.front()是您在OutlineVec中隐藏的前一个P2或P3。计算内部斜接点可能对此有所帮助,因为它将删除其他点。