戻る

名称 ・・・・・・・

ベジェ曲線に関するサブルーチンセット 概略

ルーチン名 ・・・・・・・

主サブルーチン
calcPos-------t=taの地点の座標の取得(sub_set01-001)
SimpLength----t=ta(始点)〜tb(終点)までの長さ計算(sub_set01-002)
tParam--------距離を指定してtパラメータを計算(sub_set01-003)
XyzToT------- x,y,zいずれかの座標値からtパラメータを計算(sub_set01-004)
Kyokuritu------tを指定し、その点の曲率、曲率半径を計算(sub_set01-005)
eVecSessen ---tを指定し、その点の接線の単位ベクトルを計算(sub_set01-006)
eVecHousen ---tを指定し、その点の法線の単位ベクトルを計算(sub_set01-007)
eVecJihou-----tを指定し、その点の次法線の単位ベクトルを計算(sub_set01-008)
testDrawLine---確認用のShade上へのテスト描画(sub_set01-009)

補助サブルーチン
tBibun---------tに関する座標値,微分,2階微分の計算(sub_set01-010)
sBibun--------弧長に関する微分、2階微分を計算(sub_set01-011)
GetVec3------選択している線型状の座標データを取得(sub-004)
GetnVec------ベジエ制御点P0〜P3の座標値の取得(sub_set01-012)
Formula1D----1次式の解法(sub_set01-013)
Formula2D----2次式の解法(解の公式)(sub_set01-014)
Cardano-------3次式の解法(カルダノ方式)(sub_set01-015)
kaisa---------ベジエ制御点Pから階差を計算(sub_set01-016)

スクリプトNo ・・・・・・・ sub_set01
DownLoad ・・・・・・・ scriptSet01.lzh(ANSI版)
scriptSet01_u.lzh(UTF-8版)
(右クリックのコンテクストメニューでダウンロードできます。windowsのみ)
関連するサブルーチンが全て含まれています。
圧縮されていますので解凍してreadme.htmlの説明を参考にして下さい。
概略 ・・・・・・・

ベジェ曲線に関する基本的な処理をサブルーチンにまとめてました。
主サブルーチンの項目には主な処理を、補助サブルーチンにはそれを実現させる為に必要な基本的な処理がまとめてあります。

ファイル内のMainサブルーチンで試してみたい処理の呼び出し部分(ブロック)のコメントアウトを解除すれば、試すことができます。
具体的な理論については、すでに紹介しました電子工房の加藤さんのサイトの「私家版 ベジェ講座」を是非見てください。判りやすく説明されてます。

使用前提 ・・・・・・・

実行する際は、線形状(ベジェ曲線)を選択し、形状に合った条件を引数に代入してください。

依存関係 ・・・・・・・

上記のルーチン名をクリックすると詳細情報の中に記述があります。

引数説明 ・・・・・・・

スクリプト内に記述 詳細は上記のルーチン名をクリックして下さい。

戻り値説明 ・・・・・・・

同上

変数説明 ・・・・・・・

同上

全体の説明 ・・・・・・・

「t」あるいは「tパラメータ」は、あるアンカーポイントの1区間においてのみ有効なパラメータ(0〜1の範囲)です。shadeでt=3.25といった表記は、ここではアンカー番号3〜4の区間のt=0.25を意味します。
ベジェ曲線のイメージ図を参考にして下さい。

testDrawLineサブルーチンはeVecSessen、eVecHousen、eVecJihouと組み合わせて結果をshadeにテスト描画する為のものなので、それ以外には使用(コメントアウト)しないで下さい。

※ばらばらのものをまとめたのでよりスマートに改良して使ってください。

 
スクリプト
Option Explicit
XShade.Message"====================start====================="
Call Main()
XShade.Message"====================end====================="

'================================================================================
'==  曲線(ベジェ曲線)に関する共通 main
'================================================================================

'----------------ベジェ曲線用座標データの取得
sub Main()
  Dim NA,Vec3,PtNo
  Dim Rou,a,b,c,ta,tb,n,Ltarget
  Dim eVec,Pos,L,nd
  Dim tt,NoOfT,i,DatXYZ,Direction

  'アンカーポイントの数を取得
  NA=XShade.NumberOfAnchorPoints()
  'アンカーポイントの座標値を取得
  Vec3=GetVec3(NA)

'--------------------ベジェ曲線でt=taの地点の座標を計算する。
'    (アンカーポイント番号PtNo〜PtNo+1の部分が対象)
'  ta=0.32: PtNo=0
'  Pos=calcPos(Vec3,PtNo,ta)
'  XShade.Message"x="&round(Pos(0)*1000)/1000&_
'  "   y="&round(Pos(1)*1000)/1000&_
'  "   z="&round(Pos(2)*1000)/1000

'--------------------ベジェ曲線でta(始点)からtb(終点)までの長さを計算
'    (アンカーポイント番号PtNo〜PtNo+1の部分が対象)
'  n=20: ta=0: tb=1: PtNo=0
'  b=SimpLength(Vec3,n,ta,tb,PtNo)
'  XShade.Message"ta="&ta&"から tb="&tb&"までの距離は"&_
'                 (round(b*1000)/1000)&"です。"

'--------------------ベジェ曲線上の距離を指定してtパラメータを計算する
'    (アンカーポイント番号PtNo〜PtNo+1の部分が対象)
'  PtNo=0: Ltarget=3000: n=10
'  tb=tParam(Vec3,PtNo,Ltarget,n)
'  if tb>=0 and tb<=1 then
'      XShade.Message"アンカーポイント番号="&PtNo&"から"&_
'      Ltarget&"mmの地点のtパラメータは"&round(tb*10000)/10000&"です。"
'  end if

'-------------------xyzいずれかを指定しベジェ曲線上でどのtを取るか計算
'    (アンカーポイント番号PtNo〜PtNo+1の部分が対象)
'  PtNo=0: DatXYZ=300: Direction=0
'  tt=XyzToT(PtNo,Vec3,DatXYZ,Direction)
'  if tt(0)<>false then
'    nd=ubound(tt,1)
'    XShade.Message"解の数は"&nd&"個です。"
'    for i=1 to nd
'      XShade.Message"解("&i&")は"&round(tt(i)*100000)/100000
'    next
'  end if


'==========================曲率関連=================================
'-------------------ベジェ曲線でtを指定し、その点の曲率、曲率半径を計算
'    (アンカーポイント番号PtNo〜PtNo+1の部分が対象)
'  PtNo=0: tb=0.55
'  a=Kyokuritu(Vec3,PtNo,tb)

'-------------------ベジェ曲線でtを指定し、その点の接線の単位ベクトルを計算
'    (アンカーポイント番号PtNo〜PtNo+1の部分が対象)
'  PtNo=0: tb=0.55
'  eVec=eVecSessen(Vec3,PtNo,tb)
'  if not IsArray(eVec) then exit sub

'-------------------ベジェ曲線でtを指定し、その点の法線の単位ベクトルを計算
'    (アンカーポイント番号PtNo〜PtNo+1の部分が対象)
'  PtNo=0: tb=0.55
'  eVec=eVecHousen(Vec3,PtNo,tb)
'  if not IsArray(eVec) then exit sub

'-------------------ベジェ曲線でtを指定し、その点で次法線の単位ベクトルを計算
'    (アンカーポイント番号PtNo〜PtNo+1の部分が対象)
'  PtNo=0: tb=0.55
'  eVec=eVecJihou(Vec3,PtNo,tb)
'  if not IsArray(eVec) then exit sub


'---単位ベクトルを計算したものの線分をShade上に描画する。
'  l=400
'  Pos=calcPos(Vec3,PtNo,tb)
'  Call testDrawLine(eVec,Pos,PtNo,tb,L)
End sub


'===================================================================
'=                  主要なプロシージャー(subまたはfunction)
'===================================================================

'-----------tパラメータから座標値(のみ)の計算
Function calcPos(Vec3,PtNo,ta)
'階差とtパラメータ(=ta)から座標値のみを求める。
'戻値---tパラメータtaでの座標値
'引数---
' Vec3  :(in)座標データ(アンカーポイント番号、座標軸方向[x,y,z]、ポイントの種類[アンカー,In,Out])
' PtNo  :(in)取り出す座標値のアンカーポイント番号(指定する)
' ta     :(in)求めたいtパラメータ

  Dim XYZ0(2),XYZ1(2),XYZ2(2),XYZ3(2)  '階差
  Dim Pos(2)  ':座標値用配列
  Dim P       ':3次のベジエ制御点P0〜P3の座標値
  Dim j       ':カウンタ用変数(xyz)

  P=GetnVec(Vec3,PtNo)
  Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)

  For j = 0 To 2
    Pos(j) = XYZ3(j)*ta^3+3*XYZ2(j)*ta^2+3*XYZ1(j)*ta+XYZ0(j)
  Next
  calcPos=Pos
End function


'--------------ベジェ曲線の弧長(S)を求める
'ベジェパラメーターtaからtb間での長さを求めるFunction
Function SimpLength(Vec3,n,ta,tb,PtNo)
'引数---
'  Vec3:(in)線型状の座標データ【2次元】が格納されている。
'  n   :(in)分割数(精度に関係する)
'  ta  :(in)最初のポイントのベジェパラメータ
'  tb  :(in)終点のベジェパラメータ
'  PtNo:(in)PtNo番目のアンカーポイント〜PtNo+1番目のアンカーポイントまで計算される
  dim XYZ0(3),XYZ1(3),XYZ2(3),XYZ3(3)
  Dim Pos(3), dPos(3), ddPos(3)
  Dim P
  Dim j,k,Ft1,Ft2n,Ft,SFt1,SFt2
  Dim A,B,C,h,t()
  '---引数nが決まったためtを動的に宣言
  Redim t(2*n)

  'ベジェ計算用の座標データを取り出す
  P=GetnVec(Vec3,PtNo)
  '---ベジェパラメータtを2n等分にしそれぞれのk番目のベジェパラメータを配列に割り付け
  for k=0 to 2*n
    t(k)=ta+k*(tb-ta)/(2*n)
  next

  '-----Simpson法で必要な各パラメータの算出-----
  Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)                     '階差の計算
  Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,t(0),Pos,dPos,ddPos)  'XYZ,1階微分,2階微分を計算
  Ft1=SQR(dPos(0)^2+dPos(1)^2+dPos(2)^2)
  Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)
  Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,t(2*n),Pos,dPos,ddPos)
  Ft2n=SQR(dPos(0)^2+dPos(1)^2+dPos(2)^2)

  SFt1=0
  for k=1 to n
    Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)
    Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,t(2*k-1),Pos,dPos,ddPos)
    Ft=SQR(dPos(0)^2+dPos(1)^2+dPos(2)^2)
    SFt1=SFt1+Ft
  next

  SFt2=0
  for k=1 to n-1
    Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)
    Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,t(2*k),Pos,dPos,ddPos)
    Ft=SQR(dPos(0)^2+dPos(1)^2+dPos(2)^2)
    SFt2=SFt2+Ft
  next

  'シンプソンの公式に必要なパラメータ
  A=Ft1+Ft2n	        'パラメータA
  h=(tb-ta)/(2*n)	'パラメータh
  B=4*SFt1		'パラメータB
  C=2*SFt2		'パラメータC

  'Simpsonの公式
  SimpLength=(h/3)*(A+B+C)
  XShade.Message PtNo&"番目のポイントと"&PtNo+1&"番目のポイントの長さ:S="&Round(SimpLength)
End function


'--------------tパラメータ算出(指定ポイントからの距離でtパラメーターを計算)
Function tParam(Vec3,PtNo,Ltarget,n)
'アンカーポイントからLtargetの距離にある曲線上の点のtパラメータを計算する
'ついでにそのポイントのxyz座標も参考までに計算
'戻値---tパラメーター
'引数---
' Vec3   :(in)対象となる線形状の座標データ(3次元)
' PtNo   :(in)PtNo番目のアンカーポイント〜PtNo+1番目のアンカーポイントまでが計算の対象となる。
' Ltarget:(in)アンカーポイントからの距離(指定値)
' n      :(in)分割数 弧長計算(SimpLength)で使用する。(精度に影響する)

  XShade.Message"Ltarget="&Ltarget
  Dim XYZ0(2),XYZ1(2),XYZ2(2),XYZ3(2)  '階差
  Dim ta,tb          'ta:開始tパラメータ  tb:終了tパラメータ
  Dim dLnew          'dLnew:計算値と目標値との差(長さ)
  Dim flag,alpha     'flag:判定用フラグ  alpha:収束判定値(許容誤差範囲)
  Dim P               '3次のベジエ制御点P0〜P3の座標値
  Dim Pos(2)         'ベジェ曲線上の点の座標値
  Dim dPos(2)        'Posの微分
  Dim ddPos(2)       'Posの二階微分

  ta=0:tb=1:alpha=0.0001

  P=GetnVec(Vec3,PtNo)   '3次のベジエ制御点P0〜P3の座標値を取得する
  tb=LTarget/SimpLength(Vec3,n,ta,tb,PtNo)   '弧長を求める		'
  if abs((SimpLength(Vec3,n,ta,tb,PtNo)-Ltarget))/Ltarget&gtalpha then
    Flag=0
    '---収束条件に当てはまるまで逐次計算
    Do
      Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)   '階差を求める	
      Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,tb,Pos,dPos,ddPos) 'Pos,Posの微分,二階微分を求める
      dLnew=SQR(dPos(0)^2+dPos(1)^2+dPos(2)^2)
      tb=tb+(Ltarget-SimpLength(Vec3,n,ta,tb,PtNo))/dLnew
      if abs(SimpLength(Vec3,n,ta,tb,PtNo)-LTarget)/LTarget < alpha then flag=1
    Loop until Flag
  end if
  tParam=tb
  Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,tb,Pos,dPos,ddPos)
End function


'-----------XYZのうち一つの値を渡し、ベジェ曲線と交差する交点のtパラメータを計算する(誤差あり)
Function XyzToT(PtNo,Vec3,Dat,j)
'戻値---tパラメータ
'引数---
' PtNo:(in)PtNo番目のアンカーポイント〜PtNo+1番目のアンカーポイントまでが計算の対象となる。
' Vec3:(in)対象となる線形状の座標データ(3次元配列)
' Dat :(in)次のjで指定した座標軸方向の数値(XYZのうち一つの値)
' j   :(in)指定する座標軸方向(0:x , 1:y , 2:z)
  Dim XYZ0(2),XYZ1(2),XYZ2(2),XYZ3(2)  '階差
  Dim P                  '3次のベジエ制御点P0〜P3の座標値
  Dim a,b,c              'カルダノ法用パラメータ(3次式の項の係数)
  Dim tt                 'tパラメータ(データは複数(1〜3)の場合もある)
  Dim tt2()              '範囲内のtパラメータ
  Dim nd                 '返されるtパラメータの数
  Dim nd2                '範囲内の解(tパラメータ)の数
  Dim i

  nd2=0
  redim tt2(nd2)

  P=GetnVec(Vec3,PtNo)  'ベジエ制御点P0〜P3の座標値を取り出す

  Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)  '1〜3段階差を求る

  if XYZ3(j)<>0 then           '各次元の係数で処理を振り分ける。
    a=3*XYZ2(j)/XYZ3(j)
    b=3*XYZ1(j)/XYZ3(j)
    c=(XYZ0(j)-Dat)/XYZ3(j)
    tt=Cardano(a,b,c)          '3次式の解を求める
  elseif XYZ2(j)<>0 then
   a=3*XYZ2(j)
    b=3*XYZ1(j)
    c=(XYZ0(j)-Dat)
    tt=Formula2D(a,b,c)        '2次式の解を求める
  elseif XYZ1(j)<>0 then
    a=3*XYZ1(j)
    b=(XYZ0(j)-Dat)
    tt=Formula1D(a,b)          '1次式の解を求める
  else
    XShade.message"この方向には変化量がありません"
    tt2(0)=false
    exit function
  end if

  nd=Ubound(tt,1)
  if tt(0)=false then
    XShade.message"交差ポイントはありません"
    tt2(0)=false
    exit function
  end if

  '範囲(0〜1)外の解を排除
  tt2(0)=true
  for i=1 to nd
    if tt(i)>=0 and tt(i)<=1 then 
      nd2=nd2+1
      redim preserve tt2(nd2)
      tt2(nd2)=tt(i)
    end if
  next
  if nd2=0 then         '解が0の場合はfalseを返す。
    tt2(0)=false
    exit function
  end if

  XyzToT=tt2
end Function


'--------------ベジェ曲線の曲率(κ)を求める
Function kyokuritu(Vec3,PtNo,tb)
'戻値---曲率κ
'引数---
' Vec3:(in)対象となる線形状の座標データ(3次元)
' PtNo:(in)PtNo番目のアンカーポイント〜PtNo+1番目のアンカーポイントまでが計算の対象となる。
' tb  :(in)tパラメータ(指定値)
' Pos :(out)曲線上のtb点(曲率を求めた点)の座標
  Dim XYZ0(2),XYZ1(2),XYZ2(2),XYZ3(2)  '階差
  Dim Kapper,Rou          'Kapper:曲率  Rou:曲率半径
  Dim Pos(2), dPos(2), ddPos(2)  'Posの微分、Posの二階微分(tで微分)
  Dim P                       '3次のベジエ制御点P0〜P3の座標値
  Dim sPos(2),ssPos(2)   'Posの微分、Posの二階微分(s弧長で微分)

  'ベジェ計算用の座標データを取り出す
  P=GetnVec(Vec3,PtNo)
  '階差を求めtの一階微分、二階微分を計算する
  Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)
  Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,tb,Pos,dPos,ddPos)
  'sの一階微分、二階微分を計算する
  Call sBibun(dPos,ddPos,sPos,ssPos)
  'κ(曲率)を求める公式
  kyokuritu=sqr(ssPos(0)^2+ssPos(1)^2+ssPos(2)^2)
  Rou=1/kyokuritu
  XShade.Message PtNo&"番目で、ベジェパラメータt="&tb&"のときの曲率κ="_
            &round(kyokuritu*1000000)/1000000
  XShade.Message PtNo&"番目で、ベジェパラメータt="&tb&"のときの曲率半径ρ="_
            &round(Rou*1000)/1000
End Function


'--------------ベジェ曲線の接線パラメーター
Function eVecSessen(Vec3,PtNo,tb)
'ベジェパラメータtでのベジェ曲線の接線のパラメータeVec(l,m,n)を算出する

'戻値---接線の方向ベクトル
'引数---
' PtNo:(in)PtNo番目のアンカーポイント〜PtNo+1番目のアンカーポイントまでが計算の対象となる。
' tb  :(in)tパラメータ(指定値)
' Vec3:(in)対象となる線形状の座標データ(3次元配列)
' Pos :(out)曲線上のtb点(接線を求めた点)の座標
  Dim XYZ0(2),XYZ1(2),XYZ2(2),XYZ3(2)  '階差
  Dim Kapper,Rou         'Kapper:曲率  Rou:曲率半径
  Dim Pos(2), dPos(2), ddPos(2)  'Posの微分、Posの二階微分(tで微分)
  Dim P                       '3次のベジエ制御点P0〜P3の座標値
  Dim sPos(2),ssPos(2)   'Posの微分、Posの二階微分(s弧長で微分)
  Dim eVec(2)            '接線の方向(単位)ベクトル
  Dim j,S

  P=GetnVec(Vec3,PtNo)
  '階差を求めtの一階微分、二階微分を計算する
  Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)
  Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,tb,Pos,dPos,ddPos)
  S=sqr(dPos(0)^2+dPos(1)^2+dPos(2)^2)
  If S=0 then
    eVecSessen=false:Exit Function
  end if
  for j=0 to 2
    eVec(j)=dPos(j)/S
  next
  eVecSessen=eVec
End Function


'--------------ベジェ曲線の法線パラメーター
Function eVecHousen(Vec3,PtNo,tb)
'ベジェパラメータtでのベジェ曲線の法線のパラメータeVec(l,m,n)を算出する

'戻値---法線の方向ベクトル
'引数---
' PtNo:(in)PtNo番目のアンカーポイント〜PtNo+1番目のアンカーポイントまでが計算の対象となる。
' tb  :(in)tパラメータ(指定値)
' Vec3:(in)対象となる線形状の座標データ(3次元配列)
' Pos :(out)曲線上のtb点(法線を求めた点)の座標
  Dim XYZ0(2),XYZ1(2),XYZ2(2),XYZ3(2)  '階差
  Dim Kapper,Rou          'Kapper:曲率  Rou:曲率半径
  Dim Pos(2), dPos(2), ddPos(2)  'Posの微分、Posの二階微分(tで微分)
  Dim P                       '3次のベジエ制御点P0〜P3の座標値
  Dim sPos(2),ssPos(2)   'Posの微分、Posの二階微分(s弧長で微分)
  Dim eVec(2)              '法線の方向(単位)ベクトル
  Dim j

  P=GetnVec(Vec3,PtNo)
  '階差を求めtの一階微分、二階微分を計算する
  Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)
  Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,tb,Pos,dPos,ddPos)
  'sの一階微分、二階微分を計算する
  Call sBibun(dPos,ddPos,sPos,ssPos)
  'κ(曲率)を求める公式
  Kapper=sqr(ssPos(0)^2+ssPos(1)^2+ssPos(2)^2)
  If Kapper=0 then
    eVecHousen=false:Exit Function
  end if
  if Kapper then
    Rou=1/Kapper
    for j=0 to 2
      eVec(j)=Rou*ssPos(j)	
    next
  end if
  eVecHousen=eVec
End Function 


'--------------ベジェ曲線の次法線パラメーター
Function eVecJihou(Vec3,PtNo,tb)
'ベジェパラメータtでのベジェ曲線の法線のパラメータ(l,m,n)を算出する

'戻値---法線の方向ベクトル
'引数---
' PtNo:(in)PtNo番目のアンカーポイント〜PtNo+1番目のアンカーポイントまでが計算の対象となる。
' tb  :(in)tパラメータ(指定値)
' Vec3:(in)対象となる線形状の座標データ(3次元配列)
' Pos :(out)曲線上のtb点(法線を求めた点)の座標
  Dim XYZ0(2),XYZ1(2),XYZ2(2),XYZ3(2)  '階差
  Dim Kapper,Rou          'Kapper:曲率  Rou:曲率半径
  Dim Pos(2), dPos(2), ddPos(2)  'Posの微分、Posの二階微分(tで微分)
  Dim P                       '3次のベジエ制御点P0〜P3の座標値
  Dim sPos(2),ssPos(2)   'Posの微分、Posの二階微分(s弧長で微分)
  Dim eVec(2)              '法線の方向(単位)ベクトル
  Dim j
  P=GetnVec(Vec3,PtNo)
    '階差を求めtの一階微分、二階微分を計算する
  Call kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)
  Call tBibun(XYZ0,XYZ1,XYZ2,XYZ3,tb,Pos,dPos,ddPos)
  'sの一階微分、二階微分を計算する
  Call sBibun(dPos,ddPos,sPos,ssPos)
  'κ(曲率)を求める公式
  Kapper=sqr(ssPos(0)^2+ssPos(1)^2+ssPos(2)^2)
  If Kapper=0 then
    eVecJihou=false:Exit Function
  end if
  if Kapper then
    Rou=1/Kapper
    eVec(0)=Rou*(sPos(1)*ssPos(2)-sPos(2)*ssPos(1))	
    eVec(1)=Rou*(sPos(2)*ssPos(0)-sPos(0)*ssPos(2))
    eVec(2)=Rou*(sPos(0)*ssPos(1)-sPos(1)*ssPos(0))		
  end if
  eVecJihou=eVec
End Function


'----------------------------描画テストプロシージャ
sub testDrawLine(eVec,Pos,PtNo,tb,l)
'計算された接線、法線、次法線を確認するためにtest描画する
'戻値---無し
'引数---
' eVec   :(in)方向ベクトル
' Pos    :(in)描画開始ポイントの座標値
' PtNo   :(in)アンカーポイントの番号
' tb     :(in)ベジェパラメータ(値を表示するだけに使う)
' l      :(in)描画する線分の長さ

  '除算で分母がゼロになる場合の処理
  if eVec(0) or eVec(1) or eVec(2) then
    XShade.Message"eVec=( "&round(eVec(0)*100)/100&" , "_
     &round(eVec(1)*100)/100&" , "&round(eVec(2)*100)/100&" )"
    XShade.Message"Pos=( "&round(Pos(0)*100)/100&" , "_
     &round(Pos(1)*100)/100&" , "&round(Pos(2)*100)/100&" )"
  else
    XShade.Message"方向ベクトルがゼロベクトルなので描画できません。"
  end if

  '曲線上の指定されたベジェパラメータ位置にポイントを追加する
  XShade.InsertControlPoint(PtNo+tb)

  'ラインを描画する
  XShade.StartPolygon(0)
    XShade.AppendPoint Pos(0),Pos(1),Pos(2)
    XShade.AppendPoint Pos(0)+l*eVec(0),Pos(1)+l*eVec(1),Pos(2)+l*eVec(2)
  XShade.FinishPolygon()
  XShade.SelectSister 1
End Sub

'================================================================================
'=     子プロシージャ-の記述 
'================================================================================

'-----------座標値,微分,二階微分の計算
Sub tBibun(XYZ0,XYZ1,XYZ2,XYZ3,ta,Pos,dPos,ddPos)
'階差とtパラメータ(=ta)から座標値,微分,二階微分を求める。
'戻値---無し
'引数---
' XYZ0   :(in)P0座標値(1次元、3要素の配列)
' XYZ1   :(in)第一段階差(1次元、3要素の配列)
' XYZ2   :(in)第二段階差(1次元、3要素の配列)
' XYZ3   :(in)第三段階差(1次元、3要素の配列)
' ta     :(in)求めたいtパラメータ
' Pos    :(out)tでのxyz座標値
' dPos   :(out)tでの微分値
' ddPos  :(out)tでの二階微分値

  Dim j   ':カウンタ用変数(xyz)

  'XYZ座標、一階微分、二階微分
  For j = 0 To 2
    Pos(j) = XYZ3(j)*ta^3+3*XYZ2(j)*ta^2+3*XYZ1(j)*ta+XYZ0(j)
    dPos(j) = 3*(XYZ3(j)*ta^2+2*XYZ2(j)*ta+XYZ1(j))
    ddPos(j) = 6*(XYZ3(j)*ta+XYZ2(j))
  Next
End Sub

'-------------tの微分,t2の階微分から弧長の微分、弧長の2階微分を計算
Function sBibun(d1,d2,S,SS)
'戻値---成功か失敗か(成功:true、失敗:false)
'引数---
' d1  :(in)t微分
' d2  :(in)t二階微分
' S   :(out)s微分
' SS  :(out)s二階微分
  Dim A,B,j
  A=d1(0)^2+d1(1)^2+d1(2)^2
  B=d1(0)*d2(0)+d1(1)*d2(1)+d1(2)*d2(2)
  if A=0 or B=0 then
    sBibun=false
    exit function
  end if

  for j=0 to 2
    S(j)=d1(j)/sqr(A)
    SS(j)=(d2(j)/A)-(d1(j)*B/(A^2))
  next
  sBibun=true
End Function

'---選択している線型状の座標データを取得する(3次元)
Function GetVec3(NA)
'戻値---アンカーポイントの座標データ 3次元配列
'    各次元は (アンカーポイント番号、座標軸方向[x,y,z]、ポイントの種類[アンカー,In,Out])
'引数---
' NA   :アンカーポイントの数
' 
  Dim Vec()          'アンカーポイントの座標データ(データ構造は戻り値と同じ)
  Dim i,j            'for next用カウンタ
  Redim Vec(NA,3,3)
  for i=0 to NA-1
    for j=0 to 2
      Vec(i,j,0)=XShade.AnchorPoint(i,j)
      Vec(i,j,1)=XShade.InHandle(i,j)
      Vec(i,j,2)=XShade.OutHandle(i,j)
    next
  next
  GetVec3=Vec
End Function

'------------------------------ベジェ計算に使うP0〜P3の座標をVecから拾い出す	
Function GetnVec(Vec3,PtNo)
'戻値---'3次のベジエ制御点P0〜P3の座標値(2次元)
'引数---
' Vec3  :(in)座標データ(アンカーポイント番号、座標軸方向[x,y,z]、ポイントの種類[アンカー,In,Out])
' PtNo  :(in)取り出す座標値のアンカーポイント番号(指定する)
  Dim j,P(3,2)
  for j=0 to 2 
    P(0,j)=Vec3(PtNo,j,0)                'P0:最初のポイントの座標値		
    P(1,j)=Vec3(PtNo,j,2)                'P1:最初のポイントのInハンドル座標値
    P(2,j)=Vec3(PtNo+1,j,1)              'P2:次のポイントのOutハンドル座標値
    P(3,j)=Vec3(PtNo+1,j,0)              'P3:次のポイントの座標値
  next
  GetnVec=P
End Function


'--------------1次式の解法
function Formula1D(a,b)
'1次式の解を求める。
'戻値---解(tパラメータ) 配列(1次元)0番目は成否に使用する。
'引数---
' a   :(in)1次式の1次の項の係数。
' b   :(in)1次式の定数

  Dim t(1)                '解(tパラメータ)用変数

  t(0)=true
  t(1)=-b/a
  Formula1D=t
end function

'--------------2次式の解法(解の公式)
function Formula2D(a,b,c)
'2次式の解を求める。
'戻値---解(tパラメータ) 配列(1次元)0番目は成否に使用する。
'引数---
' a   :(in)2次式の2次の項の係数
' b   :(in)2次式の1次の項の係数
' c   :(in)2次式の定数項

  Dim D                  '判別式の結果用の変数
  Dim nd                 '解の数を保管する変数
  Dim t()                '解(tパラメータ)用変数

  D=round((b^2-4*a*c)*1000000)/1000000
  if D>0 then
    nd=2
    Redim t(nd)
    t(0)=true
    t(1)=(-b+sqr(b^2-4*a*c))/(2*a)
    t(2)=(-b-sqr(b^2-4*a*c))/(2*a)
  elseif D=0 then
    nd=1
    redim t(nd)
    t(0)=true
    t(1)=(-b+sqr(b^2-4*a*c))/(2*a)
  elseif D<0 then
    nd=0
    Redim t(nd)
    t(0)=false
  end if

  Formula2D=t
end function

'--------------3次式の解法(カルダノ方式)
Function Cardano(a,b,c)
'3次式の解をカルダノの公式で求める。
'戻値---解(tパラメータ) 配列(1次元)0番目は成否に使用する。
'引数---
' a   :(in)3次式の2次の項の係数
' b   :(in)3次式の1次の項の係数
' c   :(in)3次式の定数項

  Dim D                  '判別式の結果用の変数
  Dim q,p,u,v,ux         '一時変数
  Dim nd                 '解の数を保管する変数
  Dim tt()               '解(tパラメータ)用変数

  p=-((a/3)^2)+(b/3)
  q=((a/3)^3)-(a/3)*(b/2)+(c/2)
  D=round(((q^2)+(p^3))*1000000)/1000000

  if D>0 then        '解が1つの場合
    nd=1
    Redim tt(nd)
    tt(0)=true
    if -q+sqr(q^2+p^3)>=0 then
      u=(-q+sqr(q^2+p^3))^(1/3)
    else
      u=-((-(-q+sqr(q^2+p^3)))^(1/3))
    end if
    if -q-sqr(q^2+p^3)>=0 then
      v=(-q-sqr(q^2+p^3))^(1/3)
    else
      v=-((-(-q-sqr(q^2+p^3)))^(1/3))
    end if
    tt(1)=u+v-a/3
  elseif D=0 then    '解が2つの場合
    nd=2
    Redim tt(nd)
    tt(0)=true
    if (-q)>=0 then
      tt(1)=2*(-q)^(1/3)-a/3
      tt(2)=-((-q)^(1/3))-a/3
    else
      tt(1)=-2*(-q)^(1/3)-a/3
      tt(2)=-(-q)^(1/3)-a/3
    end if
  elseif D<0 then    '解が3つの場合
    nd=3
    Redim tt(nd)
    tt(0)=true
    ux=q/(p*(-p)^(1/2))
    u=XShade.Acos(ux)
    tt(1)=2*sqr(-p)*cos(u/3)-a/3
    tt(2)=2*sqr(-p)*cos((u/3)+(2*3.14159265358979)/3)-a/3
    tt(3)=2*sqr(-p)*cos((u/3)+(4*3.14159265358979)/3)-a/3
  end if

  Cardano=tt
End Function

'-----------階差の計算(ベジエ制御点Pから階差を計算)
Sub kaisa(P,XYZ0,XYZ1,XYZ2,XYZ3)
'3次のベジエ制御点Pの座標値から1〜3段階差を計算する。
'戻値---無し
'引数---
' P      :(in)ベジエ制御点Pの座標値(1次元、3要素の配列)
' XYZ0   :(out)P0座標値(1次元、3要素の配列)
' XYZ1   :(out)第一段階差(1次元、3要素の配列)
' XYZ2   :(out)第二段階差(1次元、3要素の配列)
' XYZ3   :(out)第三段階差(1次元、3要素の配列)

  Dim DVec11(2), DVec12(2), DVec13(2)   ':第一段階差用変数
  Dim DVec21(2), DVec22(2)              ':第二段階差用変数
  Dim DVec31(2)                         ':第三段階差用変数
  Dim j                                 ':カウンタ用変数(xyz)

  '第一段階差
  For j = 0 To 2
    DVec11(j) = P(1, j) - P(0, j)
    DVec12(j) = P(2, j) - P(1, j)
    DVec13(j) = P(3, j) - P(2, j)
  Next

  '第二段階差
  For j = 0 To 2
    DVec21(j) = DVec12(j) - DVec11(j)
    DVec22(j) = DVec13(j) - DVec12(j)
  Next

  '第三段階差
  For j = 0 To 2
    DVec31(j) = DVec22(j) - DVec21(j)
  Next

  '階差表示の定義
  For j = 0 To 2
    XYZ0(j) = P(0, j)
    XYZ1(j) = DVec11(j)
    XYZ2(j) = DVec21(j)
    XYZ3(j) = DVec31(j)
  Next
End sub
使用例
個々のサブルーチンの詳細を見てください。

戻る