通过 ICAS (Inner Centered Arcs) 表示曲线
关于另一种曲线表示法的建议
引言
您认为曲线绘制的重要性是什么?
请与您的专长进行比较。
- 它通过输入点。
- 它需要更少的控制点,更少的限制。
- 它具有尽可能高的连续性。(Cn 类)
- 它具有灵活的生成点密度。
- 它支持精确的圆。
在我提供的示例程序中,以上五个属性是
- 它通过输入点。
- 它使一个控制点自动对应一对输入点。
- 它在连接点的圆弧处具有 C1 连续性。
- 它具有灵活的生成点密度。
- 它支持精确的圆。
为了快速入门,请从“如何使用它?”开始。我们每天都很忙,随时……
背景
去年,我突然获得了一个短暂而深刻的闪光,并将其发展为“内切圆弧”。我尝试了一年多,并决心将其报告给 Code Project 的各位女士们、先生们。关于新技术,请阅读另一份文档“内切圆弧 (ICAS)”。我可能最终会从只读会员毕业。
ICAS 如何工作?
它将您输入的点连接成一系列圆弧,生成一个控制点,对应您的一对点。假设您有三个点 (P1, P2, P3)。它们创建两个 ICAS,在中间点 (P2) 处具有 C1 连续性。两个 ICAS 数据 △P1C1P2,其中 C1 对应 P1、P2;△P2C2P3,其中 C2 对应 P2、P3。图 1。
当然,您以后可以编辑控制点。在我的示例程序中,控制点是链接编辑的。它们在中间点 (P2) 处保持 C1 连续性。图 2。
但这并非 ICAS 的自然特性。一个 ICAS 数据由三个点组成:起始点 (P1)、控制点 (C1) 和终点 (P2)。它基本上是三角形信息,因为其内切圆心至关重要。(内切圆心意味着内接圆的圆心。)一个 ICAS 在三角形内部生成两条圆弧,这两条圆弧在其内切圆心处具有 C1 连续性。某个过程生成密铺的三角形及其内切圆心。我们将看到每个内切圆心都位于圆弧上。图 3。抱歉我的重复,请阅读“ICAS”了解详情。我不想在这里扩展话题。这就是“内切圆弧”的起源,也是我第一次向像 Code Project 这样的开放社会解释这个主题。
现在我提到了生成点的灵活密度和精确的圆。在图中,它们具有相同的点 (P1, P2, P3)。图 4。开放曲线仅由两个 ICAS 组成,它们的密铺度(或后缀)不同;另一方面,闭合曲线由三个 ICAS 组成。
如果三角形是正三角形,并且三个控制点处于同质状态,则闭合曲线将是一个圆。(在这种情况下,控制点也构成正三角形。)此外,每个正多边形都可以构成一个外接圆。
当您以 Z 字形输入点时,我的示例程序会在您的两个点 (P2, P3) 之间插值中间点 (M)。图 5。因此,整个 S 形曲线在所有连接点处都是平滑的,没有尖角。请注意,插值并非 ICAS 的特性。这只是一个实现示例。
如果您想要曲线和直线混合,您需要在直线上绘制中间点 (P4)。图 6。我的示例程序使曲线在两个端点 (P3, P5) 处与三点直线 (P3P4P5) 相切。正如您所考虑的,这不是 ICAS 的特性,而是实现示例。
示例程序实现了什么?
我的概念非常简单
- 检查 ICAS 特性
- 寻找有效的 ICAS 输入-编辑方法
我一直在我熟悉的开发环境 MFC/View Document 架构中尝试这些概念。我主要将 C 语言函数、三角形密铺、内切圆心和控制点的计算与 GUI 部分分开编写(IcasBase.h, c)。我将它们用在 CIcas
类中(IcasFig.h, cpp)。您可能会期待一些可移植性,尽管我无法保证……
在此,我解释了控制点的计算。它需要四个点:起始点 (S)、终点 (E)、前一个起始点 (SS) 和后一个终点 (EE)。图 7。控制点 (C) 是线
。请注意,PreC 和 PostC 的精确位置并非必需。仅一个通过点 (S 或 E) 和线的方向即可完成计算。
如果四个点构成 Z 形,我们需要在中间两个点 (S, E) 之间插入一个插值点 (M)。图 8。S 形曲线同时产生两个控制点 (C1, C2)。
//
//calculate control point
int GetCntl(ICASWORK *SS, ICASWORK *S,
ICASWORK *E, ICASWORK *EE,
ICASPOINT *C1, ICASPOINT *C2, ICASPOINT *M )
{
double Vx,Vy,Sx,Sy,Ex,Ey,s,t,u;
int sts = ChkCntl(SS, S, E, EE, C1, M );//set M
if (0 > sts) return sts;//MULTIPLE check valid
Work2Vector(SS,S);//A
Work2Vector(S,E); //B
Work2Vector(E,EE);//C
//vector product
//S-SS(A) vs S-E(B)
s = S->vx * E->vy - S->vy * E->vx;
if (fabs(s) < EPS) {//M = SE middle point
return -1;//S-SS and S-E are the identical line
}
//S-E(B) vs E-EE(C)
t = E->vx * EE->vy - E->vy * EE->vx;
if (fabs(t) < EPS) {//M = SE middle point
return -2;//S-E and E-EE are the identical line
}
//E-EE(C) vs S-SS(A)
u = S->vx * EE->vy - S->vy * EE->vx;
Sx = S->ex + E->ex; //A.ex + B.ex;//unit vector
Sy = S->ey + E->ey; //A.ey + B.ey;
Ex = E->ex + EE->ex;//B.ex + C.ex;
Ey = E->ey + EE->ey;//B.ey + C.ey;
Vx = E->vx;
Vy = E->vy;
if (L_PARALLEL == SS->typ) {//point SS on the identical line
Sx = S->ex; //A.ex;
Sy = S->ey; //A.ey;
}
if (L_PARALLEL == EE->typ) {//point EE on the identical line
Ex = EE->ex;//C.ex;
Ey = EE->ey;//C.ey;
}
//identical turning direction
if (s*t > 0)
{
if (fabs(u) < EPS && (L_PARALLEL == SS->typ && L_PARALLEL == EE->typ)) {
double l = E->l * 0.5;
C1->x = S->x + S->ex * l; //A.ex * l;
C1->y = S->y + S->ey * l; //A.ey * l;
C2->x = E->x - EE->ex * l;//C.ex * l;
C2->y = E->y - EE->ey * l;//C.ey * l;
//boxy
M->x = (C1->x + C2->x)*0.5;//C1C2 middle point
M->y = (C1->y + C2->y)*0.5;
return 2;
}
s = (Ey*Vx - Ex*Vy)/(Sx*Ey - Ex*Sy);
C1->x = S->x + (Sx*s);
C1->y = S->y + (Sy*s);
return 1;
}
//vector SS-S || vector E-EE (|| = parallel)
else if (fabs(u) < EPS) {
double l = E->l * 0.5;
C1->x = S->x + S->ex * l; //Ax * l;
C1->y = S->y + S->ey * l; //Ay * l;
C2->x = E->x - EE->ex * l;//Cx * l;
C2->y = E->y - EE->ey * l;//Cy * l;
//Z fold//M = SE middle point
return 2;
}
//Z fold
else {
double Ax,Ay,Cx,Cy;
double C12x,C12y,w,v;
double l = E->l * 0.5;
w = sqrt(Sx*Sx + Sy*Sy);
v = sqrt(Ex*Ex + Ey*Ey);
Ax = Sx / w;
Ay = Sy / w;
Cx = Ex / v;
Cy = Ey / v;
C1->x = S->x + Ax * l;
C1->y = S->y + Ay * l;
C2->x = E->x - Cx * l;
C2->y = E->y - Cy * l;
C12x = C2->x - C1->x;
C12y = C2->y - C1->y;
u = Vx * C12y - Vy * C12x;
if (fabs(u) < EPS) {//fail safe//M = SE middle point
}
//cross point SE and C1C2
else {
s = ((S->y - C1->y)*C12x - C12y*(S->x - C1->x))/u;
M->x = S->x + (Vx*s);
M->y = S->y + (Vy*s);
}
return 2;
}
}
...
如何使用?
构建 ICAS.exe
我的开发环境是 Visual Studio 2008 日语版。此外,VS 6.0J 也可以,只是有一些警告。我用英语重写了注释。但主菜单项、下拉菜单仍然是日文。抱歉。
启动 ICAS.exe
在 VIEW 上单击鼠标右键。出现第一个菜单。
“ICAS Input”(ICAS 输入)和“ICAS Attribute”(ICAS 属性)
“ICAS Input”引导您输入点,要完成输入,需要“双击”。“ICAS Attribute”引导您设置常规属性。
输入曲线点
单击“ICAS Input”。鼠标光标从箭头变为十字准星。当您在点位置单击鼠标左键时,它会用红色折线跟随您的点轨迹。同时,它会用蓝色折线显示控制点。棕色折线是曲线,并且它具有初始属性(单选按钮“0”表示原始折线)。图 9。要完成输入,您需要在最后一个点位置双击。它将以初始属性闭合图形。图 10。
您可以从“ICAS Attribute”中选择您的输入样式,这仅仅是我的测试环境。图 11。编辑 ICAS 属性
在您的图形内部单击鼠标左键。它会显示您选择的内容为一个红色多边形。然后在此内部单击鼠标右键。出现第二个菜单。“Icas Information”(Icas 信息)、“Move Icas Data”(移动 Icas 数据)和“Delete Icas Data”(删除 Icas 数据)。
单击“Icas Information”以编辑选定的 ICAS 属性。例如,如果您想要一条开放曲线和三度密度顶点,您应该选择如下所示,然后单击“APPLY”(应用)。
注意第五度单选按钮表示我们使用相邻两个点的最小距离。右侧的编辑框显示其像素数。左侧的文本框显示曲线的整个逻辑长度(闭合图形)。在这种情况下,中间编辑框中的 4.0 显示了您仅半长度的曲线,带有红色网格,请检查“Param”(参数)和“APPLY”。
如果您觉得有什么不对,请尝试从“ICAS attribute edit”(ICAS 属性编辑)对话框中选择“ReCalculation”(重新计算)。此功能会清理 ICAS 数据,并根据您输入的点重新计算。这使我能够避免混乱,复杂的输入-编辑 GUI 调试。抱歉我偷工减料。
编辑 ICAS 三角形
为了检查 ICAS 的特性,我不得不尝试各种情况。因此,我编写了“ICAS Triangle”(ICAS 三角形)对话框。它对应于每个三角形。它允许您独立控制控制点、直线和曲线的混合、更改切线方向等。在目标三角形内部单击鼠标左键。它会显示您选择的内容为一个蓝色三角形。然后在此内部单击鼠标右键。出现第三个菜单。“Triangle information”(三角形信息)和“Insert Segment Point”(插入段点)。
单击“Triangle Information”以编辑选定的 ICAS 三角形。如果您选中“0x10 LEFT_TAN
”,它会更改起始切线向量。某些标志在单击“OK”(确定)按钮后不会立即更改。它们需要移动控制点才能改变形状。总之,请尝试一下。并记住“ReCalculation
”(重新计算)按钮。
结论
我长期以来一直熟悉 Bézier 曲线。有时,我会被一系列 Bézier 曲线、与圆的共存或其复杂应用程序设计弄得心烦意乱。当然,NURBS 超出了我项目的范围。
我们每天都很忙,随时随地。
所以,当我想到 ICAS 的想法时,我简直不敢相信。我仔细研究了它,并确认了其有效性。然后我有一个问题。“我是第一个吗?难道没有其他伟大的人想到这个?”然后我冲到 Google 网站,搜索了关于“inner center”(内切圆心)、“biarc”(双圆弧)、“curve passes through”(曲线通过)等所有内容,但一无所获。接着我变得非常怀疑。“每个人都知道,只有我”我问了我的同事。“这只是个晦涩的琐事,不是吗?”他说,“我不知道!”
坦白说,我认为这是一个 alpha 版本。仍然存在一些小 bug。无论如何,请尝试一下。我认为这将是一种非常有用的技术。如果您给我一些建议,我将非常感激。非常感谢。
历史
- 2011 年 11 月 12 日:初始版本