// Compute isocenter shift on verification plan public VVector ComputeIsoShift(QASettings qaSet) { verifPlnIso = qaSet.pIso; // Set iso center from settings List <Double> infFldEdges = new List <Double>(); // create list to store inferior field edge for each beam foreach (Beam bm in currPln.Beams) { if (bm.MetersetPerGy > 0) { BeamParameters bmParam = bm.GetEditableParameters(); Double collAng = bmParam.ControlPoints.First().CollimatorAngle; VRect <Double> collPos = bmParam.ControlPoints.First().JawPositions; // Compute the approximate inferior field edge position shaped by the collimator and MLC Double cosAng = Math.Cos(Math.PI * collAng / 180.0); Double sinAng = Math.Sin(Math.PI * collAng / 180.0); Double DelX = 400.0; //field edge can be defined by one X and one Y jaw Double DelY = 400.0; if (cosAng > 0.01) { DelY = -collPos.Y1 / cosAng; } if (cosAng < -0.01) { DelY = -collPos.Y2 / cosAng; } //past 90 deg, Y2 defines inferior edge if (sinAng > 0.01) { DelX = -collPos.X1 / sinAng; } //ccw rotation (respect to BEV), X1 defines inferior edge if (sinAng < -0.01) { DelX = -collPos.X2 / sinAng; } //cw rotation (respect to BEV), X2 defines inferior edge infFldEdges.Add(Math.Min(DelX, DelY)); //use the minimum between DelX and DelY as the approximate inferior field edge } } if (infFldEdges.Count == 0) { return(new VVector(Double.NaN, Double.NaN, Double.NaN)); } // no field edge found, either beam parameters invalid or no MU Double MedInfFldEdge = GetMedian(infFldEdges); if (MedInfFldEdge > qaSet.pLen) // if the field edge is longer than the length of qa phantom { verifPlnIso.z = verifPlnIso.z + Math.Ceiling(MedInfFldEdge / 10 - qaSet.pLen / 10) * 10; // shift the iso superiorly (cm as minimum shift unit) } return(verifPlnIso); }
// Create QA button is clicked private void btnCrtQA_Click(object sender, RoutedEventArgs e) { // Get the selected plan currPln = EclipseContext.Course.ExternalPlanSetups.FirstOrDefault(p => p.Id == cmbPln.SelectedItem.ToString()); // Get the name of the QA course String verifCrsID = txtbQACrs.Text; // Create a new CreateAndComputeQAPlan Object CreateAndComputeQAPlan CrtQApln = new CreateAndComputeQAPlan(currPt, currPln); // Check and/or create QA course Course verifCrs = currPt.Courses.FirstOrDefault(c => c.Id == verifCrsID); if (verifCrs == null) { msgTxt = "Creating course " + verifCrsID + "..."; ShowMessage(msgTxt); verifCrs = CrtQApln.CreateCourse(verifCrsID); } // Check if current plan already exists in verifCrs String verifPlnID = currPln.Id; if (verifCrs.PlanSetups.Any(p => p.Id == verifPlnID)) { msgTxt = "Plan " + verifPlnID + " already exists in course " + verifCrsID + "."; ShowMessage(msgTxt); return; } // Copy QA phantom struct set if not available QASettings qaSet = FindQASetbyMachine(); // find the qa setting of the machine of the current plan if (qaSet == null) { msgTxt = "Treatment machine is not set in settings or multiple machines present in plan!"; ShowMessage(msgTxt); return; } StructureSet pStructSt = currPt.StructureSets.FirstOrDefault(s => s.Id == qaSet.pStrutId); if (pStructSt == null) { msgTxt = "Copying structure set " + qaSet.pStrutId + " from patient " + qaSet.pPtId + "..."; ShowMessage(msgTxt); pStructSt = currPt.CopyImageFromOtherPatient(qaSet.pPtId, null, qaSet.pImgId); } // Create verification plan msgTxt = "Creating QA plan " + verifPlnID + "..."; ShowMessage(msgTxt); ExternalPlanSetup verifPln = CrtQApln.CreateVerificationPlan(pStructSt, verifCrs, verifPlnID); // Compute if isocenter shift is needed based on QA phantom length VVector verifPlnIso = CrtQApln.ComputeIsoShift(qaSet); // the isocenter location of the QA plan if (Double.IsNaN(verifPlnIso.z)) { msgTxt = "Cannot find field edge."; ShowMessage(msgTxt); msgTxt = "Please double check field parameters and its MU."; ShowMessage(msgTxt); return; } int shftIso = (int)(verifPlnIso.z / 10 - qaSet.pIso.z / 10); // convert iso shift to cm if (shftIso > 0) { msgTxt = "Iso-center of the QA plan will be shifted by " + shftIso.ToString() + "cm superiorly."; ShowMessage(msgTxt); } // Create beams in verification plan foreach (Beam currBm in currPln.Beams) { if (currBm.MetersetPerGy > 0) { if (currBm.ControlPoints.First().PatientSupportAngle != 0.0) { msgTxt = "Couch angle for beam " + currBm.Id + " will be set to 0.0."; ShowMessage(msgTxt); } msgTxt = "Adding beam " + currBm.Id + " to QA plan " + verifPln.Id + "..."; ShowMessage(msgTxt); Beam verifBm = CrtQApln.AddBeamToVerifPlan(currBm); if (verifBm == null) { msgTxt = "Cannot add beam " + currBm.Id + " to QA plan, please delete QA plan & try again."; ShowMessage(msgTxt); return; } } } // Compute dose in verification plan msgTxt = "Calculating dose for QA plan " + verifPln.Id + "..."; ShowMessage(msgTxt); CrtQApln.ComputeDose(); // Ready for next msgTxt = "Dose calculation completed."; ShowMessage(msgTxt); if (shftIso > 0) { msgTxt = "Please inform QA personnel:"; ShowMessage(msgTxt, Colors.Red); msgTxt = "Iso-center of QA plan is shifted by " + shftIso.ToString() + "cm."; ShowMessage(msgTxt, Colors.Red); } txtbStat.Text = "Ready."; }
// Copy QA phantom struct set if not available public StructureSet CopyImageSet(QASettings qaSet) { return(currPt.CopyImageFromOtherPatient(qaSet.pPtId, null, qaSet.pImgId)); }