サイトアイコン CAD日記

ポリラインのふくらみ(改)

2007年に書いた記事「ポリラインのふくらみ」は、おいらにとって今でも大変興味深いテーマであり、もう少し掘り下げて書いてみたくなった。
ふくらみをB、円弧の中心角をAとすると、以下の計算式が成り立つというのが核心。
B = tan( A /4 )
A = 4 × arctan( B )

AutoCADでポリラインと言えば連続線のことでしょって思うのはCADの素人。poly-lineのpolyが多くのという意味なので、多くの線=連続線となるのは自然だけどね。複数の頂点(vertex)を持っていることから、頂点をつなぐだけって思われがちだが、頂点と同じ数だけのふくらみ(bulge)を持たせることが可能。

bulgeが0なら円弧ではなくて直線。マイナス値なら時計回りで、プラス値なら反時計回り。該当の頂点から次の頂点までを円弧で結んだ場合の中心角を表現するための値。-360<中心角<360のすべてを表現できる。

円弧の表現するのに3つの値「始点、終点、ふくらみ」だけで済ましている。普通に円弧を表現するなら「中心点X、中心点Y、半径、開始角、終了角、回り方」となって情報量が大きくなるのに対して、非常に節約した方法と言える。前者がdoubleの変数5つで64bit×5=320bitに対して、後者はdoubleが7つにboolが1つだから64bit×7+1=449bit。前者は後者に対して約71%で済む。

また、始点と終点があるというのが重要であり、複数の頂点がつながっているという前提におい効果を発揮する。普通の円弧表現だと、始点と終点を求めるための演算が必要になり、演算誤差によってつながってないという判断にもなりかねない。直線に円弧を含んで閉じた領域を形づくることが効率よくできる。

この記事を書きながらAutoCADポリラインのふくらみ(bulge)についてネットで調べたら、スゲー記事を発見。
【AutoCAD】ポリラインのふくらみについて
こういう記事を書きたかったんだが、いやいやなかなかここまでは書けない。数式と図解があるからわかりやすいねぇ。とりあえず、この人のTwitterはフォローしておいた。

おいらにできることはコードを公開することくらい。以下、今書いたコードを載せておく。なお、幾何計算している関数は省略している。関数名から想像して自作されたし。

///////////////////////////////////////////
// 2つの頂点とふくらみから、円弧情報を得る
static bool getArcInfoFromBulge(DPOINT* p1, DPOINT* p2, double bulge, DPOINT& CP, double& R, double& SA, double& EA, bool& ROT )
// p1 : 1点目の頂点(IN)
// p2 : 2点目の頂点(IN)
// bulge : ふくらみ(IN)
// CP : 円弧の中心点(OUT)
// R : 円弧の半径(OUT)
// SA : 円弧の開始角(OUT)
// EA : 円弧の終了角(OUT)
// ROT : 円弧の回り方(false:反時計回り true:時計回り)(OUT)
{
	// 回り方をセット
	DPOINT dp1 = *p1;
	DPOINT dp2 = *p2;
	ROT = false;
	if (bulge < 0.0)  // マイナスの場合、始終点の交換
	{
		ROT = true;
		DPOINT w = dp1;
		dp1 = dp2;
		dp2 = w;
		bulge = -bulge;
	}
	// ふくらみから円弧の中心角を得る
	double ang = atan(bulge) * 4;

	// 2点と中心角から、円の中心と半径を求める
	double zero = 1.0e-7;
	double dis = _getDistance(&dp1, &dp2);  // // p1からp2の長さ
	double sinth = sin(0.5 * _correctAng(ang));
	if (sinth < zero)  // 中心が無限遠方にある
		return false;

	// 半径が先に決まる
	R = 0.5 * dis / sinth;

	// p1,p2 から等距離(*rad)にある点が求める中心
	DPOINT cp[2] = { 0,0, 0,0 };
	if (_crossCirCir(p1, R, &dp2, R, cp, zero) < 1)
		return false;

	// 結果をセット
	CP = cp[0];
	SA = _getAng2Pnt_X(&cp[0], &dp1);
	EA = _getAng2Pnt_X(&cp[0], &dp2);

	return true;
}
モバイルバージョンを終了