ガイドラインを利用して円弧を描きます。


ガイドラインの片方の余った長さの部分は直線になります。
円弧の計算についてはここを参考に
円弧の半径と角度を求める
・ガイドライン2辺のうち短い方の長さを選ぶ
・ガイドライン同士の成す角から円弧の中心角θが求まる
・角度θとガイドラインから三平方の定理で半径と中心点が求まる
・中心点、角度、半径から円弧を描く
ガイドラインの始点を軸とした行列をつくり、その空間で計算をしてから元の空間に戻しています。
//
// プリミティブごとに円弧を描く
// Run Over: Primitives
// input0: ガイドカーブ
//
float step = 1.0;
int npts[] = primpoints(0, @primnum);
//
// 行列
//
vector world_p0 = point(0, "P", npts[0]);
vector world_p1 = point(0, "P", npts[1]);
vector world_p2 = point(0, "P", npts[2]);
// XY平面へ変換する行列をつくる
vector tangent = normalize(world_p1 - world_p0);
vector normal = normalize(cross(tangent, world_p2 - world_p1));
vector binormal = cross(normal, tangent);
matrix worldPlane = maketransform(normal, binormal, world_p0);
matrix inversePlane = invert(worldPlane);
vector local_p0 = world_p0 * inversePlane;
vector local_p1 = world_p1 * inversePlane;
vector local_p2 = world_p2 * inversePlane;
vector v0 = normalize(local_p1 - local_p0);
vector v1 = normalize(local_p1 - local_p2);
float I = acos(dot(v0, v1*-1));
//
// 円弧が描けるか判定
//
if(I <= radians(1.0))
{
// 角度が浅い場合はベジェ曲線を描画する
int prim = addprim(0, "polyline");
int num = 100;
for(int i = 0; i < num; i++)
{
float t = i / float(num-1);
vector pos = (1-t)*(1-t)*world_p0 + 2*(1-t)*t*world_p1 + t*t*world_p2;
int pt = addpoint(0, pos);
addvertex(0, prim, pt);
}
}
else
{
// 2辺の交点座標
vector cross = local_p1;
// 開始位置を保存
vector old_p0 = local_p0;
vector old_p2 = local_p2;
// 2辺の成す角度
float theta = acos(dot(v0, -v1));
// 交点から円弧開始地点までの距離(短い方を選ぶ)
float dist_p0_cross = length(cross - local_p0);
float dist_p2_cross = length(cross - local_p2);
float length = min(dist_p0_cross, dist_p2_cross);
// 開始位置をスライドさせる
local_p0 = cross - v0 * length;
local_p2 = cross - v1 * length;
// 弦の長さ
float chord_length = length((cross - v1 * length) - (cross - v0 * length));
// 半径
float radius = chord_length / 2 / (sin(theta / 2));
// 円弧の長さ
float arc_length = radius * theta;
vector left = cross(set(0, 0, 1), v1);
// 円の外側と内側の反転
int sign = 1;
if(dot(left, v0) > 0)
sign *= -1;
// 円の中心
vector arc_center = local_p2 + normalize(left) * sign * radius;
// 円弧にポイントを配置していく
int num = (int)(arc_length / step) + 1;
float per_angle = theta / (float)(num - 1);
vector center_to_p0 = local_p0 - arc_center;
float start_angle = atan2(center_to_p0.y, center_to_p0.x);
vector point_pos[];
for(int i = 0; i < num; i++)
{
float angle = start_angle + per_angle * i * sign * -1;
point_pos[i] = set(cos(angle), sin(angle), 0) * radius + arc_center;
}
//
// ポリラインを生成する
//
int prim = addprim(0, "polyline");
// 始点
if(dist_p0_cross > dist_p2_cross)
{
int pt = addpoint(0, old_p0 * worldPlane);
addvertex(0, prim, pt);
}
// 円弧
for(int i = 0; i < num; i++)
{
int pt = addpoint(0, point_pos[i] * worldPlane);
addvertex(0, prim, pt);
}
// 終点
if(dist_p2_cross > dist_p0_cross)
{
int pt = addpoint(0, old_p2 * worldPlane);
addvertex(0, prim, pt);
}
}
removeprim(0, @primnum, 1);