我使用下面的算法生成四边形,然后将其渲染成这样的轮廓
http://img810.imageshack.us/img810/8530/uhohz.png
从图中可以看出,有时候线条太细,而它们应该总是保持相同的宽度。我的算法找到了第一条线的4
顶点,然后下一条线的顶部2
顶点是前一条线的底部2
顶点。这会创建连接的线条,但似乎并不总是有效。我该如何解决这个问题?
这是我的算法:
void OGLENGINEFUNCTIONS::GenerateLinePoly(const std::vector<std::vector<GLdouble>> &input,
std::vector<GLfloat> &output, int width)
{
output.clear();
if(input.size() < 2)
{
return;
}
int temp;
float dirlen;
float perplen;
POINTFLOAT start;
POINTFLOAT end;
POINTFLOAT dir;
POINTFLOAT ndir;
POINTFLOAT perp;
POINTFLOAT nperp;
POINTFLOAT perpoffset;
POINTFLOAT diroffset;
POINTFLOAT p0, p1, p2, p3;
for(unsigned int i = 0; i < input.size() - 1; ++i)
{
start.x = static_cast<float>(input[i][0]);
start.y = static_cast<float>(input[i][1]);
end.x = static_cast<float>(input[i + 1][0]);
end.y = static_cast<float>(input[i + 1][1]);
dir.x = end.x - start.x;
dir.y = end.y - start.y;
dirlen = sqrt((dir.x * dir.x) + (dir.y * dir.y));
ndir.x = static_cast<float>(dir.x * 1.0 / dirlen);
ndir.y = static_cast<float>(dir.y * 1.0 / dirlen);
perp.x = dir.y;
perp.y = -dir.x;
perplen = sqrt((perp.x * perp.x) + (perp.y * perp.y));
nperp.x = static_cast<float>(perp.x * 1.0 / perplen);
nperp.y = static_cast<float>(perp.y * 1.0 / perplen);
perpoffset.x = static_cast<float>(nperp.x * width * 0.5);
perpoffset.y = static_cast<float>(nperp.y * width * 0.5);
diroffset.x = static_cast<float>(ndir.x * 0 * 0.5);
diroffset.y = static_cast<float>(ndir.y * 0 * 0.5);
// p0 = start + perpoffset - diroffset
// p1 = start - perpoffset - diroffset
// p2 = end + perpoffset + diroffset
// p3 = end - perpoffset + diroffset
p0.x = start.x + perpoffset.x - diroffset.x;
p0.y = start.y + perpoffset.y - diroffset.y;
p1.x = start.x - perpoffset.x - diroffset.x;
p1.y = start.y - perpoffset.y - diroffset.y;
if(i > 0)
{
temp = (8 * (i - 1));
p2.x = output[temp + 2];
p2.y = output[temp + 3];
p3.x = output[temp + 4];
p3.y = output[temp + 5];
}
else
{
p2.x = end.x + perpoffset.x + diroffset.x;
p2.y = end.y + perpoffset.y + diroffset.y;
p3.x = end.x - perpoffset.x + diroffset.x;
p3.y = end.y - perpoffset.y + diroffset.y;
}
output.push_back(p2.x);
output.push_back(p2.y);
output.push_back(p0.x);
output.push_back(p0.y);
output.push_back(p1.x);
output.push_back(p1.y);
output.push_back(p3.x);
output.push_back(p3.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 + v1.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;
}
void OGLENGINEFUNCTIONS::GenerateLinePoly(const std::vector<std::vector<GLdouble> > &input,
std::vector<GLfloat> &output, int width)
{
output.clear();
if(input.size() < 2)
{
return;
}
float w = width / 2.0f;
//glBegin(GL_TRIANGLES);
for( size_t i = 0; i < input.size()-1; ++i )
{
POINTFLOAT cur;
cur.x = input[i][0];
cur.y = input[i][1];
POINTFLOAT nxt;
nxt.x = input[i+1][0];
nxt.y = input[i+1][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;
output.push_back(p0.x);
output.push_back(p0.y);
output.push_back(p1.x);
output.push_back(p1.y);
output.push_back(p2.x);
output.push_back(p2.y);
output.push_back(p2.x);
output.push_back(p2.y);
output.push_back(p1.x);
output.push_back(p1.y);
output.push_back(p3.x);
output.push_back(p3.y);
// only do joins when we have a prv
if( i == 0 ) continue;
POINTFLOAT prv;
prv.x = input[i-1][0];
prv.y = input[i-1][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 )
{
output.push_back(cur.x);
output.push_back(cur.y);
if( det > 0 )
{
output.push_back(round[j + 1].x);
output.push_back(round[j + 1].y);
output.push_back(round[j].x);
output.push_back(round[j].y);
}
else
{
output.push_back(round[j].x);
output.push_back(round[j].y);
output.push_back(round[j + 1].x);
output.push_back(round[j + 1].y);
}
}
}
}
6条答案
按热度按时间83qze16e1#
需要编写Eigen,但核心操作应该可以轻松Map到您使用的任何向量类。
**编辑:**屏幕截图:
fcwjkofz2#
关于:
1.将每条线向上绘制到角的内侧
1.在每个角上绘制一条与角的Angular 垂直的额外线
就像这样:
alt text http://www.geekops.co.uk/photos/0000-00-02%20%28Forum%20images%29/CorrectAngleDrawing.png
蓝色/红色代表您尝试连接的两条缐条。绿色点缐是您新增的额外缐条,用来平滑边角。上图显示内容会因为尖角而被稍微裁剪。如果这是个问题,您可以将两条连接缐条进一步向外延伸,并将额外缐条绘制得更远。
**[编辑]**我发现了我的建议中的一个缺陷。你有一些凹的部分,这将不会很好地工作。对于这些情况,你会想做一些像画一个倒角边缘代替:
alt text http://www.geekops.co.uk/photos/0000-00-02%20%28Forum%20images%29/CorrectAngleDrawing2.png
**[Edit 2]**我已经对我之前发布的代码做了一些调试。下面的代码应该更用途:
...就我所见,它看起来对凸多边形有效:-)
4ktjp1zp3#
啊,我明白了。这是因为你在重复使用旧的顶点,它们不一定与新的顶点平行。
只需通过一个简单的示例手动完成代码,其中输入点发生了90度的急转弯。旧顶点将平行于
dir
,而新顶点将垂直。如果线上的点距离足够近,则会出现如图所示的奇怪行为。没有一个“简单”的解决方案可以获得宽度一致的线条,但是如果你一次只渲染一对线条,效果会更好(例如,摆脱
i > 0
的情况)。这会给你带来一些难看的尖角,但是你不会得到任何细线。vfwfrxfs4#
你要反转第一个线段和其余线段的方向。从输出向量中提取先前值的块应该设置p0和p1点,并且每次都应该基于端点计算p2和p3。
即它应该是:
qfe3c7zg5#
您不能在当前线段上使用上一线段的偏移向量-它们垂直于与当前线段无关的对象。最好使用如下相同的偏移:
然后在每个顶点画一个圆来圆化角。如果圆不是你想要的方式,你必须改变你添加到偏移量的ndir的量--这取决于在一个顶点连接的两个线段,而不仅仅是一个。你需要确定传入和传出偏移线的交叉点。从上面开始,然后用90度或120度的Angular 放大镜头来感受一下。抱歉,现在手边没有公式。
最后,你不需要归一化perp向量,你计算它的方式将产生一个单位向量。
axkjgtzd6#
This code会呈现正确的SVG:
而不是错误的:
它比genpfault的解决方案更简单,优点是要渲染的四边形更少。
这里的每一个联系都被渲染成了乔恩·凯奇答案中的最后一张图片。