5.4 Jaw/MLC外接距離のチェック¶
目的¶
x方向のJawの位置と、最大開度のMLCの位置が規定の距離になっているかを確認します。
必要な情報¶
JawのX方向の位置(X1, X2)、MLCの位置
与えられている引数¶
PlanSetup
クラスのインスタンスplan
必要な情報へのアクセス方法¶
JawとMLCの位置は、各フィールド(Beamクラス)のコントロールポイントに定義されています。
Jawの位置情報は、VMS.TPS.Common.Model.Typs 名前空間の VRect 構造体で定義されており、この構造体はX1, X2, Y1, Y2
の4つのプロパティを持っています。
フィールドの最初のコントロールポイントのJawの位置は以下のようにして取り出すことができます。
1 2 3 4 5 6 7 8 | foreach(var beam in plan.Beams) { // 最初のコントロールポイントのJaw位置 var jawPositions = beam.ControlPoints.ElementAt(0).JawPositions; // X1座標を取り出す場合 var x1 = jawPositions.X1; } |
同じく、MLC位置情報は以下のようにアクセスできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | foreach(var beam in plan.Beams) { // MLCがない場合はアクセスしない if (beam.MLC != null) { var leafPositions = beam.ControlPoints.ElementAt(0).LeafPositions; // Leaf対の数を数える var leafPairs = leafPositions.GetLength(1); // X1側バンクの30番目のMLC位置の取り出し var x1_30 = leafPositions[0, 29]; // X2側バンクの10番目のMLC位置の取り出し var x2_10 = leafPositions[1, 9]; } } |
MLC位置情報のプロパティLeafPositions
は2次元のfloat型配列[,]
となっており、最初のインデクスがバンク(0がX1、1がX2)を表し、2番目のインデクスが各Leafの座標となります。
図にすると、以下のようになっています。
ちなみに、MLCがない場合はLeafPositions
は長さ(0,0)の2次元配列を返す仕様となっています。
必要な情報の表示¶
まずはExercise_PlanCheck_Ex1.2.cs
を開き、関数 CheckFieldFunc 内にコードを記述してみましょう。
最初のFieldのX1 Jawの位置、およびX1側30番目/X2側10番目のMLCの位置を以下のようにして表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 最初のBeamを取得 var beam = plan.Beams.ElementAt(0); var id = beam.Id; var jawPositions = beam.ControlPoints.ElementAt(0).JawPositions; var x1 = jawPositions.X1; var leafPositions = beam.ControlPoints.ElementAt(0).LeafPositions; var x1_30 = leafPositions[0, 29]; var x2_10 = leafPositions[1, 9]; MessageBox.Show(string.Format("Field ID: {0}\nX1 Jaw: {1}\nX1_30MLC: {2}\n X2_10MLC: {3}", id, x1, x1_30, x2_10)); |
実際にEclipseで確認したときの位置情報と一致しているか、別のJaw位置やMLC位置でも実行して確認してみてください。
実装¶
上記の情報を用いてJawの位置とMLC位置の確認をしていきましょう。
まずはExercise_PlanCheck_Ex1.2.cs
を開き、関数 CheckFieldFunc 内の以下の部分のコメントを外し、その下にコードを記述します。
さきほどの確認用コードは消していただいて構いません。
1 | //checkName = "Check Jaw/MLC position"
|
はじめに、Jaw位置とMLCの最大開度の規定距離と許容範囲を定義しておきましょう。今回の例では 0mm/2mm としておきます。
また、警告テキストも定義しておき、エラーが出た場合はここに文字列を足していくようにしましょう。
1 2 3 4 5 | double distJawMLCX = 0.0; double tolerance = 2.0; // 警告テキスト string checkJawMLCText = "\n"; |
Jaw位置を最大開度のリーフ位置と比較し、その距離が規定位置±2mm以内であればOKと判定します。
判定は各フィールドごとに行う必要があるため、foreach
文を使用して各フィールドごとに判定フラグを設定しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | foreach(var beam in plan.Beams) { // 各ビームごとに判定フラグを初期化します bool checkJawMLC = true; // MLCあり、かつStaticの場合のみ評価 // beam.MLCPlanType => Help参照! if (beam.MLC != null && beam.MLCPlanType == 0) { var jawPositions = beam.ControlPoints.ElementAt(0).JawPositions; var leafPositions = beam.ControlPoints.ElementAt(0).LeafPositions; var leafPairs = leafPositions.GetLength(1); // Leaf対の数 // ~~判定内容を記述~~ } // ~~結果を出力する~~ } |
このチェックを実施するためには、まずはX1側バンクのMLCの最大開度(=最小値)とX2側バンクのMLCの最大開度(=最大値)を求める必要があります。ただし、閉じているLeaf対はカウントしないようにしましょう。
for
文を使用して、素直に実装してみましょう。
~~判定内容を記述~~ の下に以下を記述していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 初期値の設定 // これより 小さ/大き ければ値を更新 float minX = 200; float maxX = -200; // Leaf対を1つずつ確認していく for (int i = 0; i < leafPairs; i++) { // 開いているLeaf対のみ調べる if (leafPositions[0, i] != leafPositions[1, i]) { minX = (minX > leafPositions[0, i]) ? leafPositions[0, i] : minX; maxX = (maxX < leafPositions[1, i]) ? leafPositions[1, i] : maxX; } } |
ここで、三項演算子 を用いました。
三項演算子
1 | value = condition ? x : y; |
condition
がtrue
ならばx
を、false
ならばy
をvalue
に代入するという演算になります。
if-else
の内部が簡単な場合、三項演算子?:
を使うことでプログラムが簡潔になります。ちなみに、この演算子は エルビス演算子 とも呼ばれます。
?:
をよく見てみてください。
MLCの最大開度位置が決まれば、あとはJaw位置と比較すればOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // X Jawの規定位置を求める // X1はMLCのマイナス側、X2はMLCのプラス側 var jawIdealX1 = minX - distJawMLCX; var jawIdealX2 = maxX + distJawMLCX; // 規定位置と許容値以上ずれている場合にエラーを出す if (Math.Abs(jawIdealX1 - jawPositions.X1) > tolerance) { checkJawMLCText += string.Format("{0} : X1 jaw should be {1:f1} cm", beam.Id, jawIdealX1/10); checkJawMLC = false; } if (Math.Abs(jawIdealX2 - jawPositions.X2) > tolerance) { checkJawMLCText += string.Format("{0} : X2 jaw should be {1:f1} cm", beam.Id, jawIdealX2/10); checkJawMLC = false; } |
最後に、~~結果を出力する~~ の下にテキスト出力すれば完成です。
1 2 3 4 5 6 7 8 | if (checkJawMLC) { oText += MakeFormatText(true, checkName + "(" + beam.Id + ")", ""); } else { oText += MakeFormatText(false, checkName, checkJawMLCText); } |