/// <summary> /// Takes two transforms and transforms the control grid of this section into the control grid space of the passed transfrom. Requires control section /// of this transform to match mapped section of adding transform /// </summary> public void Add(GridTransform transform) { //We can't map if we don't have a triangle if (transform.mapPoints.Length < 3) return; //Temp: Ensure we know the edges this.CalculateEdges(); transform.BuildDataStructures(); //Reset boundaries since they will be changed _CachedControlBounds = new GridRectangle(double.MinValue, double.MinValue, 0, 0); _CachedMappedBounds = new GridRectangle(double.MinValue, double.MinValue, 0, 0); List<AddTransformThreadObj> threadObjList = new List<AddTransformThreadObj>(); List<ManualResetEvent> doneEvents = new List<ManualResetEvent>(); List<MappingGridVector2> newPoints = new List<MappingGridVector2>(mapPoints.Length); #if DEBUG List<GridVector2> mapPointList = new List<GridVector2>(newPoints.Count); #endif // Trace.WriteLine("Starting with " + mapPoints.Length + " points", "Geometry"); // List<MappingGridVector2> newPoints = new List<MappingGridVector2>(); // Trace.WriteLine("Started GridTransform.Add with " + mapPoints.Length.ToString() + " points", "Geometry"); //Search all mapping triangles and update control points, if they fall outside the grid then discard the triangle const int PointsPerThread = 4; for (int iPoint = 0; iPoint < this.mapPoints.Length; iPoint += PointsPerThread ) { //Create a series of points for the thread to process so they aren't constantly hitting the queue lock looking for new work. List<int> listPoints = new List<int>(PointsPerThread); for (int iAddPoint = iPoint; iAddPoint < iPoint + PointsPerThread; iAddPoint++) { //Don't add if the point is out of range if (iAddPoint >= this.mapPoints.Length) break; listPoints.Add(iAddPoint); } //MappingGridVector2 mapPoint = mapPoints[iPoint]; AddTransformThreadObj AddThreadObj = new AddTransformThreadObj(listPoints.ToArray(), this, transform); doneEvents.Add(AddThreadObj.DoneEvent); threadObjList.Add(AddThreadObj); /* MappingGridVector2 UnmappedPoint = mapPoints[iPoint]; MappingGridTriangle mapTri = transform.GetTransform(mapPoints[iPoint].ControlPoint); if (mapTri != null) { GridVector2 newControl = mapTri.Transform(UnmappedPoint.ControlPoint); newPoints.AddRange(new MappingGridVector2[] { new MappingGridVector2(newControl, UnmappedPoint.MappedPoint) }); List<MappingGridVector2> sortPoints = new List<MappingGridVector2>(newPoints); sortPoints.Sort(); for (int i = 1; i < sortPoints.Count; i++) { Debug.Assert(sortPoints[i - 1].ControlPoint != sortPoints[i].ControlPoint); } } else { //In this case we need to test each edge connecting this point to other points. //newPoints = new MappingGridVector2[0]; for(int i = 0; i < TriangleIndicies.Length; i += 3) { if(TriangleIndicies[i] == iPoint) { int[] EdgesIndicies = Array.Find<int>(this.TriangleIndicies, iPoint); } */ //For single threaded debug, comment out threadpool and uncomment AddThreadObj.ThreadPoolCallback line ThreadPool.QueueUserWorkItem(AddThreadObj.ThreadPoolCallback); // AddThreadObj.ThreadPoolCallback(null); //newPoints.AddRange(AddThreadObj.newPoints); //newPoints.Sort(); #if false for (int iTest = 1; iTest < newPoints.Count; iTest++) { Debug.Assert(newPoints[iTest - 1].ControlPoint != newPoints[iTest].ControlPoint); } for (int iMap = 0; iMap < AddThreadObj.newPoints.Length; iMap++) { mapPointList.Add(AddThreadObj.newPoints[iMap].MappedPoint); } mapPointList.Sort(); for (int iMap = 1; iMap < mapPointList.Count; iMap++) { Debug.Assert(GridVector2.Distance(mapPointList[iMap], mapPointList[iMap - 1]) > Global.epsilon); } #endif } //Wait for the threads to finish processing. There is a 64 handle limit for WaitAll so we wait on one at a time foreach (ManualResetEvent doneEvent in doneEvents) doneEvent.WaitOne(); newPoints.Clear(); foreach(AddTransformThreadObj obj in threadObjList) { if(obj.newPoints != null) newPoints.AddRange(obj.newPoints); } // Trace.WriteLine("Mapped " + newPoints.Count + " points", "Geometry"); #if false mapPointList.Clear(); for (int iMap = 0; iMap < newPoints.Count; iMap++) { mapPointList.Add(newPoints[iMap].MappedPoint); } mapPointList.Sort(); for (int iMap = 1; iMap < mapPointList.Count; iMap++) { Debug.Assert(GridVector2.Distance(mapPointList[iMap], mapPointList[iMap - 1]) > Global.epsilon); } #endif //Remove duplicates: In the case that a line on the warpingGrid passes through a point on the fixedGrid then both ends of the line will map the point and we will get a duplicate newPoints.Sort(); int iCompareStart = 0; for (int iTest = 1; iTest < newPoints.Count; iTest++) { // Debug.Assert(newPoints[iTest - 1].ControlPoint != newPoints[iTest].ControlPoint); //This is slow, but even though we sort on the X axis it doesn't mean a point that is not adjacent to the point on the list isn't too close for (int jTest = iCompareStart; jTest < iTest; jTest++) { if (GridVector2.Distance(newPoints[jTest].ControlPoint, newPoints[iTest].ControlPoint) <= Global.Epsilon) { newPoints.RemoveAt(iTest); iTest--; break; } //Optimization, since the array is sorted we don't need to compare points once a point is distant enough if (newPoints[iTest].ControlPoint.X - newPoints[jTest].ControlPoint.X > Global.Epsilon) { iCompareStart = jTest; } } } // Trace.WriteLine("Ended with " + newPoints.Count + " points", "Geometry"); this.mapPoints = newPoints.ToArray(); //Edges are build on mapPoints, so we need to remove them so they'll be recalculates _edges = null; //Other datastructures are dependent on edges, so minimize memory will delete them MinimizeMemory(); // Trace.WriteLine("Finished GridTransform.Add with " + newPoints.Count.ToString() + " points", "Geometry"); //Check whether these have been set yet or if I don't need to clear them again this.ControlSection = transform.ControlSection; }
object ICloneable.Clone() { GridTransform newObj = new GridTransform(); newObj = this.MemberwiseClone() as GridTransform; List<MappingGridVector2> TempList = new List<MappingGridVector2>(); foreach (MappingGridVector2 pt in mapPoints) { TempList.Add((MappingGridVector2)pt.Copy()); } //Setting the mapPoints will sort and recalculate triangles newObj.mapPoints = TempList.ToArray(); return newObj; }
public static GridRectangle CalculateBounds(GridTransform[] transforms) { double minX = double.MaxValue; double minY = double.MaxValue; double maxX = double.MinValue; double maxY = double.MinValue; foreach (GridTransform T in transforms) { GridRectangle R = T.CachedControlBounds; if (R.Left < minX) minX = R.Left; if (R.Right > maxX) maxX = R.Right; if (R.Bottom < minY) minY = R.Bottom; if (R.Top > maxY) maxY = R.Top; } return new GridRectangle(minX, maxX, minY, maxY); }