NURBS 曲線の knot 値( Bスプライン基底関数を決定する為の値 )は、一般的に下記のように ( 階数 + 制御点数 ) 個の単調増加する配列として表されます。
T = [ 0 0 0 0 1 2 3 3 3 3 ]
一方、OpenCASCADE Draw Test Harness の bsplinecurve コマンドにて上に示す knot 値を持つ NURBS 曲線を生成するには、下記のように指定する必要があります。
0 4 1 1 2 1 3 4
太字部分は knot 値自体を示すのではなく、”直前にある knot 値をくりかえす回数” を示します。Draw Test Harness の公式リファレンスでは誤った記載で引数の解説がなされていた為、少し迷いました。仮に、前者を Plain knots array、後者を Compressed knots array と呼ぶと、下記のような相互変換を行う Perl スクリプトが組めます。
#!/usr/bin/env perl
# Plain knots array to Compressed knots array
# for Draw Test Harness of OpenCASCADE
sub pKnot2cKnot {
my @pKnot = @_;
my @cKnot = ( );
my $bfr = -1, $nb = 0, $i = 0;
foreach $val ( @pKnot ) {
if ( $val != $bfr ) {
$cKnot[$i++] = $nb if ( $i > 0 );
$cKnot[$i++] = $bfr = $val;
$nb = 1;
}
else {
$nb++;
}
}
$cKnot[$i] = $nb;
return @cKnot;
}
# Compressed knots array to Plain knots array
sub cKnot2pKnot {
while ( length( $val = shift( @_ ) ) ) {
$buf .= ( "$val " x ( shift( @_ ) ) );
}
return split( " ", $buf );
}
このような仕組みを使って、ある三次元 CAD 形式のファイルから Draw Test Harness 経由で、BRep モデルを作成する事が出来ました。ひょっとしたら pKnot2cKnot() は、もう少しスマートに出来るかも。
2020/07/02 追記
度重なるブログ移転・ブログシステムのアップデートにより崩れた記事を校正。ついでに Ruby で書き直してみました。
#!/usr/bin/ruby
def pknot2cknot(str)
ar = []
c = 1
x = nil
str.split(' ').each_cons(2) do |n, m|
if n != m
ar << [n, c]
c = 1
else
x = n
c += 1
end
end
(ar << [x, c]).join(' ')
end
def cknot2pknot(str)
Hash[*str.split(' ')].map{|n, m|"#{n} "*m.to_i}.join.strip
end
p pknot2cknot("0 0 0 0 1 2 3 3 3 3") # => "0 4 1 1 2 1 3 4"
p cknot2pknot("0 4 1 1 2 1 3 4") # => "0 0 0 0 1 2 3 3 3 3"
劇的に小さくなると思いきや、そんなことなかったのが残念。
Array#partition でも Array#group_by でもなく、配列から隣合う要素を比較し、区切りを見つけて多次元配列を返すメソッド…ないかなぁ。