private bool createTSStructures() { if (RemoveOldTSStructures(TS_structures)) { return(true); } if (scleroTrial) { if (RemoveOldTSStructures(scleroStructures)) { return(true); } } //determine if any TS structures need to be added to the selected structure set (i.e., were not present or were removed in the first foreach loop) //this is provided here to only add additional TS if they are relevant to the current case (i.e., it doesn't make sense to add the brain TS's if we //are not interested in sparing brain) foreach (Tuple <string, string, double> itr in spareStructList) { optParameters.Add(Tuple.Create(itr.Item1, itr.Item2)); if (itr.Item1.ToLower().Contains("lungs")) { foreach (Tuple <string, string> itr1 in TS_structures.Where(x => x.Item2.ToLower().Contains("lungs"))) { AddTSStructures(itr1); } //do NOT add the scleroStructures to the addedStructures vector as these will be handled manually! if (scleroTrial) { if (selectedSS.CanAddStructure("CONTROL", "Lung_Block_L")) { selectedSS.AddStructure("CONTROL", "Lung_Block_L"); } if (selectedSS.CanAddStructure("CONTROL", "Lung_Block_R")) { selectedSS.AddStructure("CONTROL", "Lung_Block_R"); } if (selectedSS.CanAddStructure("CONTROL", "Lungs_Eval")) { selectedSS.AddStructure("CONTROL", "Lungs_Eval"); } } } else if (itr.Item1.ToLower().Contains("liver")) { foreach (Tuple <string, string> itr1 in TS_structures.Where(x => x.Item2.ToLower().Contains("liver"))) { AddTSStructures(itr1); } } else if (itr.Item1.ToLower().Contains("brain")) { foreach (Tuple <string, string> itr1 in TS_structures.Where(x => x.Item2.ToLower().Contains("brain"))) { AddTSStructures(itr1); } } else if (itr.Item1.ToLower().Contains("kidneys")) { foreach (Tuple <string, string> itr1 in TS_structures.Where(x => x.Item2.ToLower().Contains("kidneys"))) { AddTSStructures(itr1); } //do NOT add the scleroStructures to the addedStructures vector as these will be handled manually! if (scleroTrial) { if (selectedSS.CanAddStructure("CONTROL", "Kidney_Block_R")) { selectedSS.AddStructure("CONTROL", "Kidney_Block_R"); } if (selectedSS.CanAddStructure("CONTROL", "Kidney_Block_L")) { selectedSS.AddStructure("CONTROL", "Kidney_Block_L"); } } } } if (scleroTrial) { foreach (Tuple <string, string> itr in scleroStructures) { Structure tmp = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == itr.Item2.ToLower()); Structure tmp1 = null; if (itr.Item2.ToLower().Contains("lung_block_l")) { //AxisAlignedMargins(inner or outer margin, margin from negative x, margin for negative y, margin for negative z, margin for positive x, margin for positive y, margin for positive z) tmp1 = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "lung_l"); if (tmp1 != null) { tmp.SegmentVolume = tmp1.AsymmetricMargin(new AxisAlignedMargins( StructureMarginGeometry.Inner, 10.0, 10.0, 15.0, 10.0, 10.0, 10.0)); } } else if (itr.Item2.ToLower().Contains("lung_block_r")) { tmp1 = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "lung_r"); if (tmp1 != null) { tmp.SegmentVolume = tmp1.AsymmetricMargin(new AxisAlignedMargins( StructureMarginGeometry.Inner, 10.0, 10.0, 15.0, 10.0, 10.0, 10.0)); } } else if (itr.Item2.ToLower().Contains("lungs_eval")) { tmp1 = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "lung_block_l"); Structure tmp2 = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "lung_block_r"); if (tmp1 != null && tmp2 != null) { tmp.SegmentVolume = tmp2.Or(tmp1.Margin(0.0)); } } else if (itr.Item2.ToLower().Contains("kidney_block_l")) { tmp1 = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "kidney_l"); if (tmp1 != null) { tmp.SegmentVolume = tmp1.AsymmetricMargin(new AxisAlignedMargins( StructureMarginGeometry.Outer, 5.0, 20.0, 20.0, 20.0, 20.0, 20.0)); } } else { tmp1 = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "kidney_r"); if (tmp1 != null) { tmp.SegmentVolume = tmp1.AsymmetricMargin(new AxisAlignedMargins( StructureMarginGeometry.Outer, 20.0, 20.0, 20.0, 5.0, 20.0, 20.0)); } } } } //now contour the various structures foreach (string s in addedStructures) { Structure tmp = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == s.ToLower()); //MessageBox.Show(s); if (!(s.ToLower().Contains("ptv"))) { Structure tmp1 = null; double margin = 0.0; if (s.ToLower().Contains("human")) { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "body"); } else if (s.ToLower().Contains("lungs")) { if (selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "lungs_lowres") == null) { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "lungs"); } else { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "lungs_lowres"); } } else if (s.ToLower().Contains("liver")) { if (selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "liver_lowres") == null) { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "liver"); } else { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "liver_lowres"); } } else if (s.ToLower().Contains("kidneys")) { if (selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "kidneys_lowres") == null) { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "kidneys"); } else { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "kidneys_lowres"); } } else if (s.ToLower().Contains("brain")) { if (selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "brain_lowres") == null) { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "brain"); } else { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "brain_lowres"); } } //all structures in TS_structures and scleroStructures are inner margins, which is why the below code works. int pos1 = s.IndexOf("-"); int pos2 = s.IndexOf("cm"); if (pos1 != -1 && pos2 != -1) { double.TryParse(s.Substring(pos1, pos2 - pos1), out margin); } //convert from cm to mm tmp.SegmentVolume = tmp1.Margin(margin * 10); } else if (s.ToLower() == "ptv_body") { //get the body contour and create the ptv structure using the user-specified inner margin Structure tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "body"); tmp.SegmentVolume = tmp1.Margin(-targetMargin * 10); //subtract all the structures the user wants to spare from PTV_Body foreach (Tuple <string, string, double> spare in spareStructList) { if (spare.Item2 == "Mean Dose < Rx Dose") { if (spare.Item1.ToLower() == "kidneys" && scleroTrial) { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "kidney_block_r"); tmp.SegmentVolume = tmp.Sub(tmp1.Margin(0.0)); tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "kidney_block_l"); tmp.SegmentVolume = tmp.Sub(tmp1.Margin(0.0)); } else { tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == spare.Item1.ToLower()); tmp.SegmentVolume = tmp.Sub(tmp1.Margin((spare.Item3) * 10)); } } } } else if (s.ToLower() == "ts_ptv_vmat") { //copy the ptv_body contour onto the TS_ptv_vmat contour Structure tmp1 = selectedSS.Structures.First(x => x.Id.ToLower() == "ptv_body"); tmp.SegmentVolume = tmp1.Margin(0.0); //matchplane exists and needs to be cut from TS_PTV_Body. Also remove all TS_PTV_Body segements inferior to match plane if (selectedSS.Structures.Where(x => x.Id.ToLower() == "matchline").Any()) { //find the image plane where the matchline is location. Record this value and break the loop. Also find the first slice where the ptv_body contour starts and record this value Structure matchline = selectedSS.Structures.First(x => x.Id.ToLower() == "matchline"); bool lowLimNotFound = true; int lowLim = -1; if (!matchline.IsEmpty) { int matchplaneLocation = 0; for (int i = 0; i != selectedSS.Image.ZSize - 1; i++) { if (matchline.GetContoursOnImagePlane(i).Any()) { matchplaneLocation = i; break; } if (lowLimNotFound && tmp1.GetContoursOnImagePlane(i).Any()) { lowLim = i; lowLimNotFound = false; } } if (selectedSS.Structures.Where(x => x.Id.ToLower() == "dummybox").Any()) { selectedSS.RemoveStructure(selectedSS.Structures.First(x => x.Id.ToLower() == "dummybox")); } Structure dummyBox = selectedSS.AddStructure("CONTROL", "DummyBox"); //get min/max positions of ptv_body contour to contour the dummy box for creating TS_PTV_Legs Point3DCollection ptv_bodyPts = tmp1.MeshGeometry.Positions; double xMax = ptv_bodyPts.Max(p => p.X) + 50.0; double xMin = ptv_bodyPts.Min(p => p.X) - 50.0; double yMax = ptv_bodyPts.Max(p => p.Y) + 50.0; double yMin = ptv_bodyPts.Min(p => p.Y) - 50.0; //box with contour points located at (x,y), (x,0), (x,-y), (0,-y), (-x,-y), (-x,0), (-x, y), (0,y) VVector[] pts = new[] { new VVector(xMax, yMax, 0), new VVector(xMax, 0, 0), new VVector(xMax, yMin, 0), new VVector(0, yMin, 0), new VVector(xMin, yMin, 0), new VVector(xMin, 0, 0), new VVector(xMin, yMax, 0), new VVector(0, yMax, 0) }; //give 5cm margin on TS_PTV_LEGS (one slice of the CT should be 5mm) in case user wants to include flash up to 5 cm for (int i = matchplaneLocation - 1; i > lowLim - 10; i--) { dummyBox.AddContourOnImagePlane(pts, i); } //do the structure manipulation if (selectedSS.Structures.Where(x => x.Id.ToLower() == "ts_ptv_legs").Any()) { selectedSS.RemoveStructure(selectedSS.Structures.First(x => x.Id.ToLower() == "ts_ptv_legs")); } Structure TS_legs = selectedSS.AddStructure("CONTROL", "TS_PTV_Legs"); TS_legs.SegmentVolume = dummyBox.And(tmp.Margin(0)); //subtract both dummybox and matchline from TS_PTV_VMAT tmp.SegmentVolume = tmp.Sub(dummyBox.Margin(0.0)); tmp.SegmentVolume = tmp.Sub(matchline.Margin(0.0)); //remove the dummybox structure if flash is NOT being used as its no longer needed if (!useFlash) { selectedSS.RemoveStructure(dummyBox); } } } } } return(false); }
private bool preliminaryChecks() { //check if user origin was set //get the points collection for the Body (used for calculating number of isocenters) Point3DCollection pts = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "body").MeshGeometry.Positions; if (!selectedSS.Image.HasUserOrigin || !(selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "body").IsPointInsideSegment(selectedSS.Image.UserOrigin))) { MessageBox.Show("Did you forget to set the user origin? \nUser origin is NOT inside body contour! \nPlease fix and try again!"); return(true); } //check if patient length is > 116cm, if so, check for matchline contour if ((pts.Max(p => p.Z) - pts.Min(p => p.Z)) > 1160.0 && !(selectedSS.Structures.Where(x => x.Id.ToLower() == "matchline").Any())) { //check to see if the user wants to proceed even though there is no matchplane contour or the matchplane contour exists, but is not filled VMATTBIautoPlan.confirmUI CUI = new VMATTBIautoPlan.confirmUI(); CUI.message.Text = "No matchplane contour found even though patient length > 116.0 cm!" + Environment.NewLine + Environment.NewLine + "Continue?!"; CUI.ShowDialog(); if (!CUI.confirm) { return(true); } //checks for LA16 couch and spinning manny couch/bolt will be performed at optimization stage } //calculate number of required isocenters if (!(selectedSS.Structures.Where(x => x.Id.ToLower() == "matchline").Any())) { //no matchline implying that this patient will be treated with VMAT only. For these cases the maximum number of allowed isocenters is 3. //the reason for the explicit statements calculating the number of isos and then truncating them to 3 was to account for patients requiring < 3 isos and if, later on, we want to remove the restriction of 3 isos numIsos = numVMATIsos = (int)Math.Ceiling(((pts.Max(p => p.Z) - pts.Min(p => p.Z)) / (400.0 - 20.0))); if (numIsos > 3) { numIsos = numVMATIsos = 3; } } else { //matchline structure is present, but empty if (selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").IsEmpty) { VMATTBIautoPlan.confirmUI CUI = new VMATTBIautoPlan.confirmUI(); CUI.message.Text = "I found a matchline structure in the structure set, but it's empty!" + Environment.NewLine + Environment.NewLine + "Do you want to continue without using the matchline structure?!"; CUI.ShowDialog(); if (!CUI.confirm) { return(true); } //continue and ignore the empty matchline structure (same calculation as VMAT only) numIsos = numVMATIsos = (int)Math.Ceiling(((pts.Max(p => p.Z) - pts.Min(p => p.Z)) / (400.0 - 20.0))); if (numIsos > 3) { numIsos = numVMATIsos = 3; } } //matchline structure is present and not empty else { //get number of isos for PTV superior to matchplane (always truncate this value to a maximum of 3 isocenters) numVMATIsos = (int)Math.Ceiling(((pts.Max(p => p.Z) - selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z) / (400.0 - 20.0))); if (numVMATIsos > 3) { numVMATIsos = 3; } //get number of iso for PTV inferior to matchplane //if (selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z - pts.Min(p => p.Z) - 3.0 <= (400.0 - 20.0)) numIsos = numVMATIsos + 1; //5-20-2020 Nataliya said to only add a second legs iso if the extent of the TS_PTV_LEGS is > 40.0 cm if (selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z - pts.Min(p => p.Z) - 3.0 <= (400.0 - 0.0)) { numIsos = numVMATIsos + 1; } else { numIsos = numVMATIsos + 2; } //MessageBox.Show(String.Format("{0}", selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z - pts.Min(p => p.Z) - 3.0)); } } //set isocenter names based on numIsos and numVMATIsos (determined these names from prior cases). Need to implement a more clever way to name the isocenters isoNames.Add("Head"); if (numVMATIsos == 2) { isoNames.Add("Pelvis"); } else if (numVMATIsos == 3 && numIsos > numVMATIsos) { isoNames.Add("Chest"); isoNames.Add("Pelvis"); } //this could technically be an else statement, but I left it as an else-if statement so it's explicit what situation is being considered here else if (numVMATIsos == 3 && numIsos == numVMATIsos) { isoNames.Add("Pelvis"); isoNames.Add("Legs"); } if (numIsos > numVMATIsos) { isoNames.Add("AP / PA upper legs"); if (numIsos == numVMATIsos + 2) { isoNames.Add("AP / PA lower legs"); } } //check if selected structures are empty or of high-resolution (i.e., no operations can be performed on high-resolution structures) string output = "The following structures are high-resolution:" + System.Environment.NewLine; List <Structure> highResStructList = new List <Structure> { }; List <Tuple <string, string, double> > highResSpareList = new List <Tuple <string, string, double> > { }; foreach (Tuple <string, string, double> itr in spareStructList) { if (itr.Item2 == "Mean Dose < Rx Dose") { if (selectedSS.Structures.First(x => x.Id == itr.Item1).IsEmpty) { MessageBox.Show(String.Format("Error! \nThe selected structure that will be subtracted from PTV_Body and TS_PTV_VMAT is empty! \nContour the structure and try again.")); return(true); } else if (selectedSS.Structures.First(x => x.Id == itr.Item1).IsHighResolution) { highResStructList.Add(selectedSS.Structures.First(x => x.Id == itr.Item1)); highResSpareList.Add(itr); output += String.Format("{0}", itr.Item1) + System.Environment.NewLine; } } } //if there are high resolution structures, they will need to be converted to default resolution. if (highResStructList.Count() > 0) { //ask user if they are ok with converting the relevant high resolution structures to default resolution output += "They must be converted to default resolution before proceeding!"; VMATTBIautoPlan.confirmUI CUI = new VMATTBIautoPlan.confirmUI(); CUI.message.Text = output + Environment.NewLine + Environment.NewLine + "Continue?!"; CUI.message.Font = new System.Drawing.Font("Microsoft Sans Serif", 11F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); CUI.ShowDialog(); if (!CUI.confirm) { return(true); } int count = 0; foreach (Structure s in highResStructList) { //get the high res structure mesh geometry MeshGeometry3D mesh = s.MeshGeometry; //get the start and stop image planes for this structure int startSlice = (int)((mesh.Bounds.Z - selectedSS.Image.Origin.z) / selectedSS.Image.ZRes); int stopSlice = (int)(((mesh.Bounds.Z + mesh.Bounds.SizeZ) - selectedSS.Image.Origin.z) / selectedSS.Image.ZRes) + 1; //create an Id for the low resolution struture that will be created. The name will be '_lowRes' appended to the current structure Id string newName = s.Id + "_lowRes"; if (newName.Length > 16) { newName = newName.Substring(0, 16); } //add a new structure (default resolution by default) Structure lowRes = null; if (selectedSS.CanAddStructure("CONTROL", newName)) { lowRes = selectedSS.AddStructure("CONTROL", newName); } else { MessageBox.Show(String.Format("Error! Cannot add new structure: {0}!\nCorrect this issue and try again!", newName.Substring(0, 16))); return(true); } //foreach slice that contains contours, get the contours, and determine if you need to add or subtract the contours on the given image plane for the new low resolution structure. You need to subtract contours if the points lie INSIDE the current structure contour. //We can sample three points (first, middle, and last points in array) to see if they are inside the current contour. If any of them are, subtract the set of contours from the image plane. Otherwise, add the contours to the image plane. NOTE: THIS LOGIC ASSUMES //THAT YOU DO NOT OBTAIN THE CUTOUT CONTOUR POINTS BEFORE THE OUTER CONTOUR POINTS (it seems that ESAPI generally passes the main structure contours first before the cutout contours, but more testing is needed) //string data = ""; for (int slice = startSlice; slice < stopSlice; slice++) { VVector[][] points = s.GetContoursOnImagePlane(slice); for (int i = 0; i < points.GetLength(0); i++) { //for (int j = 0; j < points[i].GetLength(0); j++) data += String.Format("{0}, {1}, {2}", points[i][j].x, points[i][j].y, points[i][j].z) + System.Environment.NewLine; if (lowRes.IsPointInsideSegment(points[i][0]) || lowRes.IsPointInsideSegment(points[i][points[i].GetLength(0) - 1]) || lowRes.IsPointInsideSegment(points[i][(int)(points[i].GetLength(0) / 2)])) { lowRes.SubtractContourOnImagePlane(points[i], slice); } else { lowRes.AddContourOnImagePlane(points[i], slice); } //data += System.Environment.NewLine; } } //get the index of the high resolution structure in the structure sparing list and repace this entry with the newly created low resolution structure int index = spareStructList.IndexOf(highResSpareList.ElementAt(count)); spareStructList.RemoveAt(index); spareStructList.Insert(index, new Tuple <string, string, double>(newName, highResSpareList.ElementAt(count).Item2, highResSpareList.ElementAt(count).Item3)); count++; //data += System.Environment.NewLine; //points = lowRes.GetContoursOnImagePlane((int)((stopSlice + startSlice) / 2)); //for (int i = 0; i < points.GetLength(0); i++) //{ // for (int j = 0; j < points[i].GetLength(0); j++) data += String.Format("{0}, {1}, {2}", points[i][j].x, points[i][j].y, points[i][j].z) + System.Environment.NewLine; // //data += System.Environment.NewLine; //} //File.WriteAllText(@"\\enterprise.stanfordmed.org\depts\RadiationTherapy\Public\Users\ESimiele\vvectorData.txt", data); } //inform the main UI class that the UI needs to be updated updateSparingList = true; //string mod = ""; //foreach (Tuple<string, string, double> itr in spareStructList) mod += String.Format("{0}, {1}, {2}", itr.Item1, itr.Item2, itr.Item3) + System.Environment.NewLine; //MessageBox.Show(test); //MessageBox.Show(mod); } return(false); }
public Point3DCollection GetCoordinatesForBoundingBox(Point3DCollection positions) { const double distance = 10.0; const double half = 2.0; var minX = positions.Min(x => x.X); var minY = positions.Min(x => x.Y); var minZ = positions.Min(x => x.Z); var maxX = positions.Max(x => x.X); var maxY = positions.Max(x => x.Y); var maxZ = positions.Max(x => x.Z); return(new Point3DCollection { //Upper new Point3D { X = (maxX + minX) / half, Y = (maxY + minY) / half, Z = maxZ }, new Point3D { X = (maxX + minX) / half, Y = (maxY + minY) / half, Z = maxZ + distance }, //Left new Point3D { X = (maxX + minX) / half, Y = maxY, Z = (maxZ + minZ) / half }, new Point3D { X = (maxX + minX) / half, Y = maxY + distance, Z = (maxZ + minZ) / half }, //Down new Point3D { X = (maxX + minX) / half, Y = (maxY + minY) / half, Z = minZ }, new Point3D { X = (maxX + minX) / half, Y = (maxY + minY) / half, Z = minZ - distance }, //Face new Point3D { X = maxX, Y = (maxY + minY) / half, Z = (minZ + maxZ) / half }, new Point3D { X = maxX + distance, Y = (maxY + minY) / half, Z = (minZ + maxZ) / half }, //Right new Point3D { X = (maxX + minX) / half, Y = minY, Z = (minZ + maxZ) / half }, new Point3D { X = (maxX + minX) / half, Y = minY - distance, Z = (minZ + maxZ) / half }, //Back new Point3D { X = minX, Y = (minY + maxY) / half, Z = (minZ + maxZ) / half }, new Point3D { X = minX - distance, Y = (minY + maxY) / half, Z = (minZ + maxZ) / half } }); }
private void Button1_Click(object sender, RoutedEventArgs e) { #region End if (Turn == 255) { MessageBox.Show("Finished!"); return; } #endregion #region Turn variables ///<summary> ///Turn variable, Disk 1 turn variable and 3DPoint variables ///</summary> ++Turn; Disk1Turn = Disk1Turn * -1; Point3D point1 = new Point3D(x1, y1, z1); Point3D point2 = new Point3D(x2, y2, z2); Point3D point3 = new Point3D(x3, y3, z3); Point3D point4 = new Point3D(x4, y4, z4); Point3D point5 = new Point3D(x5, y5, z5); Point3D point6 = new Point3D(x6, y6, z6); Point3D point7 = new Point3D(x7, y7, z7); Point3D point8 = new Point3D(x8, y8, z8); #endregion #region First Turn if (Turn == 1) { list_X0.Add(point3); list_X0.Add(point4); list_X0.Add(point5); list_X0.Add(point6); list_X0.Add(point7); list_X0.Add(point8); } #endregion #region Disk 1 if (Disk1Turn > 0) { x1++; if (x1 == 3) { x1 = 0; } list_X0.Remove(point1); list_X1.Remove(point1); list_X2.Remove(point1); point1.X = x1; if (x1 == 0) { if (list_X0.Count > 0) { y1 = list_X0.Min(y => y.Y) - 1; } else { y1 = 7; } point1.Y = y1; list_X0.Add(point1); } else if (x1 == 1) { if (list_X1.Count > 0) { y1 = list_X1.Min(y => y.Y) - 1; } else { y1 = 7; } point1.Y = y1; list_X1.Add(point1); } else { if (list_X2.Count > 0) { y1 = list_X2.Min(y => y.Y) - 1; } else { y1 = 7; } point1.Y = y1; list_X2.Add(point1); } Grid.SetColumn(Disk1, x1); int yy1 = (int)y1; Grid.SetRow(Disk1, yy1); } #endregion #region Disk 2 if ((Turn - 2) % 4 == 0 || Turn == 2) { list_X0.Remove(point2); list_X1.Remove(point2); list_X2.Remove(point2); x2 = x1 + 1; if (x2 == 3) { x2 = 0; } point2.X = x2; if (x2 == 0) { if (list_X0.Count > 0) { y2 = list_X0.Min(y => y.Y) - 1; } else { y2 = 7; } point2.Y = y2; list_X0.Add(point2); } else if (x2 == 1) { if (list_X1.Count > 0) { y2 = list_X1.Min(y => y.Y) - 1; } else { y2 = 7; } point2.Y = y2; list_X1.Add(point2); } else { if (list_X2.Count > 0) { y2 = list_X2.Min(y => y.Y) - 1; } else { y2 = 7; } point2.Y = y2; list_X2.Add(point2); } Grid.SetColumn(Disk2, x2); int yy2 = (int)y2; Grid.SetRow(Disk2, yy2); } #endregion #region Other Disks if (Turn % 4 == 0) { #region Variables ///<sumarry> ///Variables to see wich disk should turn and from to wich pole it should turn ///Also calculates the vertical position as in if there are disks beneath the postion the disk goes to ///</sumarry> if (!list_X0.Contains(point1)) { if (list_X0.Any()) { zx0 = list_X0.Min(z => z.Z); } else { zx0 = 9; } } else { zx0 = 10; } if (!list_X1.Contains(point1)) { if (list_X1.Any()) { zx1 = list_X1.Min(z => z.Z); } else { zx1 = 9; } } else { zx1 = 10; } if (!list_X2.Contains(point1)) { if (list_X2.Any()) { zx2 = list_X2.Min(z => z.Z); } else { zx2 = 9; } } else { zx2 = 10; } KleinsteZ = Math.Min(zx0, Math.Min(zx1, zx2)); #endregion switch (KleinsteZ) { #region Disk 3 case 3: if (zx0 == KleinsteZ) { list_X0.Remove(point3); if (Math.Min(zx1, zx2) == zx1) { x3 = 1; if (zx1 != 9) { y3 = list_X1.Min(y => y.Y) - 1; } else { y3 = 7; } point3.X = x3; point3.Y = y3; list_X1.Add(point3); } else { x3 = 2; if (zx2 != 9) { y3 = list_X2.Min(y => y.Y) - 1; } else { y3 = 7; } point3.X = x3; point3.Y = y3; list_X2.Add(point3); } } else if (zx1 == KleinsteZ) { list_X1.Remove(point3); if (Math.Min(zx0, zx2) == zx0) { x3 = 0; if (zx0 != 9) { y3 = list_X0.Min(y => y.Y) - 1; } else { y3 = 7; } point3.X = x3; point3.Y = y3; list_X0.Add(point3); } else { x3 = 2; if (zx2 != 9) { y3 = list_X2.Min(y => y.Y) - 1; } else { y3 = 7; } point3.X = x3; point3.Y = y3; list_X2.Add(point3); } } else { list_X2.Remove(point3); if (Math.Min(zx0, zx1) == zx0) { x3 = 0; if (zx0 != 9) { y3 = list_X0.Min(y => y.Y) - 1; } else { y3 = 7; } point3.X = x3; point3.Y = y3; list_X0.Add(point3); } else { x3 = 1; if (zx1 != 9) { y3 = list_X1.Min(y => y.Y) - 1; } else { y3 = 7; } point3.X = x3; point3.Y = y3; list_X1.Add(point3); } } Grid.SetColumn(Disk3, x3); int yy3 = (int)y3; Grid.SetRow(Disk3, yy3); break; #endregion #region Disk 4 case 4: if (zx0 == KleinsteZ) { list_X0.Remove(point4); if (Math.Min(zx1, zx2) == zx1) { x4 = 1; if (zx1 != 9) { y4 = list_X1.Min(y => y.Y) - 1; } else { y4 = 7; } point4.X = x4; point4.Y = y4; list_X1.Add(point4); } else { x4 = 2; if (zx2 != 9) { y4 = list_X2.Min(y => y.Y) - 1; } else { y4 = 7; } point4.X = x4; point4.Y = y4; list_X2.Add(point4); } } else if (zx1 == KleinsteZ) { list_X1.Remove(point4); if (Math.Min(zx0, zx2) == zx0) { x4 = 0; if (zx0 != 9) { y4 = list_X0.Min(y => y.Y) - 1; } else { y4 = 7; } point4.X = x4; point4.Y = y4; list_X0.Add(point4); } else { x4 = 2; if (zx2 != 9) { y4 = list_X2.Min(y => y.Y) - 1; } else { y4 = 7; } point4.X = x4; point4.Y = y4; list_X2.Add(point4); } } else { list_X2.Remove(point4); if (Math.Min(zx0, zx1) == zx0) { x4 = 0; if (zx0 != 9) { y4 = list_X0.Min(y => y.Y) - 1; } else { y4 = 7; } point4.X = x4; point4.Y = y4; list_X0.Add(point4); } else { x4 = 1; if (zx1 != 9) { y4 = list_X1.Min(y => y.Y) - 1; } else { y4 = 7; } point4.X = x4; point4.Y = y4; list_X1.Add(point4); } } Grid.SetColumn(Disk4, x4); int yy4 = (int)y4; Grid.SetRow(Disk4, yy4); break; #endregion #region Disk 5 case 5: if (zx0 == KleinsteZ) { list_X0.Remove(point5); if (Math.Min(zx1, zx2) == zx1) { x5 = 1; if (zx1 != 9) { y5 = list_X1.Min(y => y.Y) - 1; } else { y5 = 7; } point5.X = x5; point5.Y = y5; list_X1.Add(point5); } else { x5 = 2; if (zx2 != 9) { y5 = list_X2.Min(y => y.Y) - 1; } else { y5 = 7; } point5.X = x5; point5.Y = y5; list_X2.Add(point5); } } else if (zx1 == KleinsteZ) { list_X1.Remove(point5); if (Math.Min(zx0, zx2) == zx0) { x5 = 0; if (zx0 != 9) { y5 = list_X0.Min(y => y.Y) - 1; } else { y5 = 7; } point5.X = x5; point5.Y = y5; list_X0.Add(point5); } else { x5 = 2; if (zx2 != 9) { y5 = list_X2.Min(y => y.Y) - 1; } else { y5 = 7; } point5.X = x5; point5.Y = y5; list_X2.Add(point5); } } else { list_X2.Remove(point5); if (Math.Min(zx0, zx1) == zx0) { x5 = 0; if (zx0 != 9) { y5 = list_X0.Min(y => y.Y) - 1; } else { y5 = 7; } point5.X = x5; point5.Y = y5; list_X0.Add(point5); } else { x5 = 1; if (zx1 != 9) { y5 = list_X1.Min(y => y.Y) - 1; } else { y5 = 7; } point5.X = x5; point5.Y = y5; list_X1.Add(point5); } } Grid.SetColumn(Disk5, x5); int yy5 = (int)y5; Grid.SetRow(Disk5, yy5); break; #endregion #region Disk 6 case 6: if (zx0 == KleinsteZ) { list_X0.Remove(point6); if (Math.Min(zx1, zx2) == zx1) { x6 = 1; if (zx1 != 9) { y6 = list_X1.Min(y => y.Y) - 1; } else { y6 = 7; } point6.X = x6; point6.Y = y6; list_X1.Add(point6); } else { x6 = 2; if (zx2 != 9) { y6 = list_X2.Min(y => y.Y) - 1; } else { y6 = 7; } point6.X = x6; point6.Y = y6; list_X2.Add(point6); } } else if (zx1 == KleinsteZ) { list_X1.Remove(point6); if (Math.Min(zx0, zx2) == zx0) { x6 = 0; if (zx0 != 9) { y6 = list_X0.Min(y => y.Y) - 1; } else { y6 = 7; } point6.X = x6; point6.Y = y6; list_X0.Add(point6); } else { x6 = 2; if (zx2 != 9) { y6 = list_X2.Min(y => y.Y) - 1; } else { y6 = 7; } point6.X = x6; point6.Y = y6; list_X2.Add(point6); } } else { list_X2.Remove(point6); if (Math.Min(zx0, zx1) == zx0) { x6 = 0; if (zx0 != 9) { y6 = list_X0.Min(y => y.Y) - 1; } else { y6 = 7; } point6.X = x6; point6.Y = y6; list_X0.Add(point6); } else { x6 = 1; if (zx1 != 9) { y6 = list_X1.Min(y => y.Y) - 1; } else { y6 = 7; } point6.X = x6; point6.Y = y6; list_X1.Add(point6); } } Grid.SetColumn(Disk6, x6); int yy6 = (int)y6; Grid.SetRow(Disk6, yy6); break; #endregion #region Disk 7 case 7: if (zx0 == KleinsteZ) { list_X0.Remove(point7); if (Math.Min(zx1, zx2) == zx1) { x7 = 1; if (zx1 != 9) { y7 = list_X1.Min(y => y.Y) - 1; } else { y7 = 7; } point7.X = x7; point7.Y = y7; list_X1.Add(point7); } else { x7 = 2; if (zx2 != 9) { y7 = list_X2.Min(y => y.Y) - 1; } else { y7 = 7; } point7.X = x7; point7.Y = y7; list_X2.Add(point7); } } else if (zx1 == KleinsteZ) { list_X1.Remove(point7); if (Math.Min(zx0, zx2) == zx0) { x7 = 0; if (zx0 != 9) { y7 = list_X0.Min(y => y.Y) - 1; } else { y7 = 7; } point7.X = x7; point7.Y = y7; list_X0.Add(point7); } else { x7 = 2; if (zx2 != 9) { y7 = list_X2.Min(y => y.Y) - 1; } else { y7 = 7; } point7.X = x7; point7.Y = y7; list_X2.Add(point7); } } else { list_X2.Remove(point7); if (Math.Min(zx0, zx1) == zx0) { x7 = 0; if (zx0 != 9) { y7 = list_X0.Min(y => y.Y) - 1; } else { y7 = 7; } point7.X = x7; point7.Y = y7; list_X0.Add(point7); } else { x7 = 1; if (zx1 != 9) { y7 = list_X1.Min(y => y.Y) - 1; } else { y7 = 7; } point7.X = x7; point7.Y = y7; list_X1.Add(point7); } } Grid.SetColumn(Disk7, x7); int yy7 = (int)y7; Grid.SetRow(Disk7, yy7); break; #endregion #region Disk 8 case 8: if (zx0 == KleinsteZ) { list_X0.Remove(point8); if (Math.Min(zx1, zx2) == zx1) { x8 = 1; if (zx1 != 9) { y8 = list_X1.Min(y => y.Y) - 1; } else { y8 = 7; } point8.X = x8; point8.Y = y8; list_X1.Add(point8); } else { x8 = 2; if (zx2 != 9) { y8 = list_X2.Min(y => y.Y) - 1; } else { y8 = 7; } point8.X = x8; point8.Y = y8; list_X2.Add(point8); } } else if (zx1 == KleinsteZ) { list_X1.Remove(point8); if (Math.Min(zx0, zx2) == zx0) { x8 = 0; if (zx0 != 9) { y8 = list_X0.Min(y => y.Y) - 1; } else { y8 = 7; } point8.X = x8; point8.Y = y8; list_X0.Add(point8); } else { x8 = 2; if (zx2 != 9) { y8 = list_X2.Min(y => y.Y) - 1; } else { y8 = 7; } point8.X = x8; point8.Y = y8; list_X2.Add(point8); } } else { list_X2.Remove(point8); if (Math.Min(zx0, zx1) == zx0) { x8 = 0; if (zx0 != 9) { y8 = list_X0.Min(y => y.Y) - 1; } else { y8 = 7; } point8.X = x8; point8.Y = y8; list_X0.Add(point8); } else { x8 = 1; if (zx1 != 9) { y8 = list_X1.Min(y => y.Y) - 1; } else { y8 = 7; } point8.X = x8; point8.Y = y8; list_X1.Add(point8); } } Grid.SetColumn(Disk8, x8); int yy8 = (int)y8; Grid.SetRow(Disk8, yy8); break; #endregion } } #endregion #region Show turn at label BindingExpression be = Turn_count.GetBindingExpression(TextBox.TextProperty); Turn_count.Text = Turn.ToString(); be.UpdateSource(); #endregion }
/// <summary> /// Gets 2D shape of the mesh. Checks all verticies regardless of z axis. Uses a grid-based approach: /// breaks an area up into x-y grid and if it detects one or more points in that square, it is considered filled /// The countour is around these filled squares /// </summary> /// <param name="points">The points from a mesh</param> /// <param name="size">The resolution size</param> /// <returns>List of 2D points for the contour</returns> private List <Point> GetContour(Point3DCollection points, double size) { if (points.Count > 0) { double offset = 3; //used to give some space at the start of the contour boxes double bottom_x, top_x, bottom_y, top_y; bottom_x = points.Min(p => p.X); top_x = points.Max(p => p.X) - bottom_x; bottom_y = points.Min(p => p.Y); top_y = points.Max(p => p.Y) - bottom_y; double gridsize = size; double start_x = bottom_x - gridsize - offset; double start_y = bottom_y - gridsize - offset; int x_grid = Convert.ToInt16((top_x - start_x) / gridsize) + 1; int y_grid = Convert.ToInt16((top_y - start_y) / gridsize) + 1; bool[,] contourgrid = new bool[x_grid, y_grid]; //finds which box areas contain the model's positions for (int y = 0; y < y_grid; y++) { double lowY = start_y + y * gridsize; double highY = lowY + gridsize; Point3DCollection points_y = new Point3DCollection(); foreach (Point3D p in points) { if (p.Y > lowY && p.Y < highY) { points_y.Add(p); } } for (int x = 0; x < x_grid; x++) { double lowX = start_x + x * gridsize; double highX = lowX + gridsize; foreach (Point3D p in points_y) { if (p.X > lowX && p.X < highX) { contourgrid[x, y] = true; break; } } } } FillHoles(contourgrid); //create contour points by finding boxes that are adjacent to the edge of the good boxes List <Point> contourPointsList = new List <Point>(); for (int y = 0; y < y_grid; y++) { for (int x = 0; x < x_grid; x++) { int sides = AdjacentGoodBoxes(x, y, contourgrid); if (sides > 0 && sides < 4 && !contourgrid[x, y])//beside a good box and not a good box itself { Point point = new Point(start_x + x * gridsize + gridsize / 2, start_y + y * gridsize + gridsize / 2); contourPointsList.Add(point); } } } return(contourPointsList); } return(null); }
//function used to cnotour the overlap between fields in adjacent isocenters for the VMAT Plan ONLY! //this option is requested by the user by selecting the checkbox on the main UI on the beam placement tab private void contourFieldOverlap(List <VVector> isoLocations) { //grab the image and get the z resolution and dicom origin (we only care about the z position of the dicom origin) Image image = selectedSS.Image; double zResolution = image.ZRes; VVector dicomOrigin = image.Origin; //center position between adjacent isocenters, number of image slices to contour on, start image slice location for contouring List <Tuple <double, int, int> > overlap = new List <Tuple <double, int, int> > { }; //calculate the center position between adjacent isocenters, number of image slices to contour on based on overlap and with additional user-specified margin (from main UI) //and the slice where the contouring should begin //string output = ""; for (int i = 1; i < numVMATIsos; i++) { //calculate the center position between adjacent isocenters. NOTE: this calculation works from superior to inferior! double center = isoLocations.ElementAt(i - 1).z + (isoLocations.ElementAt(i).z - isoLocations.ElementAt(i - 1).z) / 2; //this is left as a double so I can cast it to an int in the second overlap item and use it in the calculation in the third overlap item double numSlices = Math.Ceiling(400.0 + contourOverlapMargin - Math.Abs(isoLocations.ElementAt(i).z - isoLocations.ElementAt(i - 1).z)); overlap.Add(new Tuple <double, int, int>( center, (int)(numSlices / zResolution), (int)(Math.Abs(dicomOrigin.z - center + numSlices / 2) / zResolution))); //add a new junction structure (named TS_jnx<i>) to the stack. Contours will be added to these structure later jnxs.Add(selectedSS.AddStructure("CONTROL", string.Format("TS_jnx{0}", i))); //output += String.Format("{0}, {1}, {2}\n", // isoLocations.ElementAt(i - 1).z + (isoLocations.ElementAt(i).z - isoLocations.ElementAt(i - 1).z) / 2, // (int)Math.Ceiling((410.0 - Math.Abs(isoLocations.ElementAt(i).z - isoLocations.ElementAt(i - 1).z)) / zResolution), // (int)(Math.Abs(dicomOrigin.z - (isoLocations.ElementAt(i - 1).z + ((isoLocations.ElementAt(i).z - isoLocations.ElementAt(i - 1).z) / 2)) + Math.Ceiling((410.0 - Math.Abs(isoLocations.ElementAt(i).z - isoLocations.ElementAt(i - 1).z))/2)) / zResolution)); } //MessageBox.Show(output); //make a box at the min/max x,y positions of the target structure with 5 cm margin Point3DCollection targetPts = target.MeshGeometry.Positions; double xMax = targetPts.Max(p => p.X) + 50.0; double xMin = targetPts.Min(p => p.X) - 50.0; double yMax = targetPts.Max(p => p.Y) + 50.0; double yMin = targetPts.Min(p => p.Y) - 50.0; VVector[] pts = new[] { new VVector(xMax, yMax, 0), new VVector(xMax, 0, 0), new VVector(xMax, yMin, 0), new VVector(0, yMin, 0), new VVector(xMin, yMin, 0), new VVector(xMin, 0, 0), new VVector(xMin, yMax, 0), new VVector(0, yMax, 0) }; //add the contours to each relevant plan for each structure in the jnxs stack int count = 0; foreach (Tuple <double, int, int> value in overlap) { for (int i = value.Item3; i < (value.Item3 + value.Item2); i++) { jnxs.ElementAt(count).AddContourOnImagePlane(pts, i); } //only keep the portion of the box contour that overlaps with the target jnxs.ElementAt(count).SegmentVolume = jnxs.ElementAt(count).And(target.Margin(0)); count++; } }