コード例 #1
0
ファイル: PLYIO.cs プロジェクト: robotsrulz/Sardauscan
		private static void Write(StreamWriter w, Point3D point)
		{
			string line = string.Format("{0} {1} {2} {3} {4}", point.Position.Dump(),
																																						point.Normal.Dump(),
																																						point.Color.R, point.Color.G, point.Color.B);
			w.WriteLine(line);
		}
コード例 #2
0
ファイル: Point3D.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary>
		/// Distance to another Point3D
		/// </summary>
		/// <param name="other"></param>
		/// <returns></returns>
		public double DistanceSquared(Point3D other)
		{
			double dx = Position.X - other.Position.X;
			double dy = Position.Y - other.Position.Y;
			double dz = Position.Z - other.Position.Z;
			return dx * dx + dy * dy + dz * dz;
		}
コード例 #3
0
ファイル: XYZIO.cs プロジェクト: robotsrulz/Sardauscan
		private static void Write(StreamWriter w, Point3D point)
		{
			//			string line = string.Format("{0} {1} {2} {3} {4} {5} {6} {7} {8}", point.Position.X, point.Position.Y, point.Position.Z,
			//																																						point.Normal.X, point.Normal.Y, point.Normal.Z,
			//																																						point.Color.R, point.Color.G, point.Color.B);
			string line = point.Position.Dump();
			w.WriteLine(line);
		}
コード例 #4
0
ファイル: Triangle3D.cs プロジェクト: robotsrulz/Sardauscan
				/// <summary>
				/// Ctor
				/// </summary>
				/// <param name="p1"></param>
				/// <param name="p2"></param>
				/// <param name="p3"></param>
				/// <param name="color"></param>
				public Triangle3D(Point3D p1, Point3D p2, Point3D p3, Vector3d normal, Color color)
        {
            Point1 = p1;
            Point2 = p2;
            Point3 = p3;
            Normal = normal;
            this.Color = color;
        }
コード例 #5
0
			/// <summary>
			/// Ctor
			/// </summary>
			/// <param name="p1"></param>
			/// <param name="p2"></param>
			/// <param name="p3"></param>
			/// <param name="color"></param>
        public Triangle3D(Point3D p1, Point3D p2, Point3D p3, Color color)
            : this(p1, p2, p3, CalculateNormal(p1, p2, p3), color)
        {
        }
コード例 #6
0
ファイル: Point3D.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary>
		/// Average this point with another
		/// </summary>
		/// <param name="other"></param>
		/// <returns></returns>
		public Point3D Average(Point3D other)
		{
			Vector3d vp1 = this.Position;
			Vector3d vp2 = other.Position;

			Vector3d np1 = this.Normal;
			Vector3d np2 = other.Normal;

			Vector4 cp1 = this.Color.ToVector();
			Vector4 cp2 = other.Color.ToVector();

			vp1 = (vp2 + vp1) / 2f;
			np1 = (np2 + np1) / 2f;
			cp1 = (cp2 + cp1) / 2f;
			return new Point3D(vp1, np1, ColorExtension.ColorFromVector(cp1));
		}
コード例 #7
0
ファイル: IntervalHeap.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary> 
		/// Remove the item with the smallest key from the queue. 
		/// </summary> 
		public void RemoveMin()
		{
			// Check for errors. 
			if (Size == 0)
				throw new Exception();

			// Remove the item by  
			Size--;
			tData[0] = tData[Size];
			tKeys[0] = tKeys[Size];
			tData[Size] = new Point3D();
			SiftDownMin(0);
		}
コード例 #8
0
			/// <summary>
			/// Ctor
			/// </summary>
			/// <param name="p1"></param>
			/// <param name="p2"></param>
			/// <param name="p3"></param>
			/// <param name="normal"></param>
        public Triangle3D(Point3D p1, Point3D p2, Point3D p3,Vector3d normal)
            : this(p1, p2, p3, normal, CalculateColor(p1, p2, p3))
        {
        }
コード例 #9
0
ファイル: KDNode.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary> 
		/// Insert a new point into this leaf node. 
		/// </summary> 
		/// <param name="tPoint">The position which represents the data.</param> 
		/// <param name="kValue">The value of the data.</param> 
		public void AddPoint(Point3D kValue)
		{
			Vector3d tPoint = kValue.Position;
			// Find the correct leaf node. 
			KDNode pCursor = this;
			while (!pCursor.IsLeaf)
			{
				// Extend the size of the leaf. 
				pCursor.ExtendBounds(tPoint);
				pCursor.Size++;

				// If it is larger select the right, or lower,  select the left. 
				if (tPoint[pCursor.iSplitDimension] > pCursor.fSplitValue)
				{
					pCursor = pCursor.pRight;
				}
				else
				{
					pCursor = pCursor.pLeft;
				}
			}

			// Insert it into the leaf. 
			pCursor.AddLeafPoint(tPoint, kValue);
		}
コード例 #10
0
        /**
            Caluclates a point on the Bezier curve represented with the four controlpoints given.
        */
        private Point3D CalculateBezierPoint(double t, Point3D p0, Point3D p1, Point3D p2, Point3D p3)
        {
            double u = 1 - t;
            double tt = t * t;
            double uu = u * u;
            double uuu = uu * u;
            double ttt = tt * t;

            Vector3d p = uuu * p0.Position; //first term

            p += 3 * uu * t * p1.Position; //second term
            p += 3 * u * tt * p2.Position; //third term
            p += ttt * p3.Position; //fourth term


            Vector3d n = uuu * p0.Normal; //first term

            n += 3 * uu * t * p1.Normal; //second term
            n += 3 * u * tt * p2.Normal; //third term
            n += ttt * p3.Normal; //fourth term

            Vector4 c = ((float)uuu) * p0.Color.ToVector(); //first term

            c += ((float)(3 * uu * t)) * p1.Color.ToVector(); //second term
            c += ((float)(3 * u * tt)) * p2.Color.ToVector(); //third term
            c += ((float)(ttt)) * p3.Color.ToVector(); //fourth term


            return new Point3D(p, n, ColorExtension.ColorFromVector(c));

        }
コード例 #11
0
ファイル: IntervalHeap.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary> 
		/// Remove the item with the largest key in the queue. 
		/// </summary> 
		public void RemoveMax()
		{
			// If we have no items in the queue. 
			if (Size == 0)
			{
				throw new Exception();
			}

			// If we have one item, remove the min. 
			else if (Size == 1)
			{
				RemoveMin();
				return;
			}

			// Remove the max. 
			Size--;
			tData[1] = tData[Size];
			tKeys[1] = tKeys[Size];
			tData[Size] = new Point3D();
			SiftDownMax(1);
		}
コード例 #12
0
ファイル: IntervalHeap.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary> 
		/// Replace the item with the smallest key in the queue. 
		/// </summary> 
		/// <param name="key">The new minimum key.</param> 
		/// <param name="value">The new minumum data value.</param> 
		public void ReplaceMin(double key, Point3D value)
		{
			// Check for errors. 
			if (Size == 0)
				throw new Exception();

			// Add the data. 
			tData[0] = value;
			tKeys[0] = key;

			// If we have more than one item. 
			if (Size > 1)
			{
				// Swap with pair if necessary. 
				if (tKeys[1] < key)
					Swap(0, 1);
				SiftDownMin(0);
			}
		}
コード例 #13
0
		/// <summary> 
		/// Check for the next iterator item. 
		/// </summary> 
		/// <returns>True if we have one, false if not.</returns> 
		public bool MoveNext()
		{
			// Bail if we are finished. 
			if (iPointsRemaining == 0)
			{
				_Current = new Point3D();
				return false;
			}

			// While we still have paths to evaluate. 
			while (pPending.Size > 0 && (pEvaluated.Size == 0 || (pPending.MinKey < pEvaluated.MinKey)))
			{
				// If there are pending paths possibly closer than the nearest evaluated point, check it out 
				KDNode pCursor = pPending.Min;
				pPending.RemoveMin();

				// Descend the tree, recording paths not taken 
				while (!pCursor.IsLeaf)
				{
					KDNode pNotTaken;

					// If the seach point is larger, select the right path. 
					if (tSearchPoint[pCursor.iSplitDimension] > pCursor.fSplitValue)
					{
						pNotTaken = pCursor.pLeft;
						pCursor = pCursor.pRight;
					}
					else
					{
						pNotTaken = pCursor.pRight;
						pCursor = pCursor.pLeft;
					}

					// Calculate the shortest distance between the search point and the min and max bounds of the kd-node. 
					double fDistance = kDistanceFunction.DistanceToRectangle(tSearchPoint, pNotTaken.tMinBound, pNotTaken.tMaxBound);

					// If it is greater than the threshold, skip. 
					if (fThreshold >= 0 && fDistance > fThreshold)
					{
						//pPending.Insert(fDistance, pNotTaken); 
						continue;
					}

					// Only add the path we need more points or the node is closer than furthest point on list so far. 
					if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey)
					{
						pPending.Insert(fDistance, pNotTaken);
					}
				}

				// If all the points in this KD node are in one place. 
				if (pCursor.bSinglePoint)
				{
					// Work out the distance between this point and the search point. 
					double fDistance = kDistanceFunction.Distance(pCursor.tPoints[0], tSearchPoint);

					// Skip if the point exceeds the threshold. 
					// Technically this should never happen, but be prescise. 
					if (fThreshold >= 0 && fDistance >= fThreshold)
						continue;

					// Add the point if either need more points or it's closer than furthest on list so far. 
					if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey)
					{
						for (int i = 0; i < pCursor.Size; ++i)
						{
							// If we don't need any more, replace max 
							if (pEvaluated.Size == iPointsRemaining)
								pEvaluated.ReplaceMax(fDistance, pCursor.tData[i]);

							// Otherwise insert. 
							else
								pEvaluated.Insert(fDistance, pCursor.tData[i]);
						}
					}
				}
				// If the points in the KD node are spread out. 
				else
				{
					// Treat the distance of each point seperately. 
					for (int i = 0; i < pCursor.Size; ++i)
					{
						// Compute the distance between the points. 
						double fDistance = kDistanceFunction.Distance(pCursor.tPoints[i], tSearchPoint);

						// Skip if it exceeds the threshold. 
						if (fThreshold >= 0 && fDistance >= fThreshold)
							continue;

						// Insert the point if we have more to take. 
						if (pEvaluated.Size < iPointsRemaining)
							pEvaluated.Insert(fDistance, pCursor.tData[i]);

						// Otherwise replace the max. 
						else if (fDistance < pEvaluated.MaxKey)
							pEvaluated.ReplaceMax(fDistance, pCursor.tData[i]);
					}
				}
			}

			// Select the point with the smallest distance. 
			if (pEvaluated.Size == 0)
				return false;

			iPointsRemaining--;
			_CurrentDistance = pEvaluated.MinKey;
			_Current = pEvaluated.Min;
			pEvaluated.RemoveMin();
			return true;
		}
コード例 #14
0
ファイル: IqrFilter.cs プロジェクト: robotsrulz/Sardauscan
 public double GetVal(Point3D pt)
 {
     return pt.Position.Xz.Length;
 }
コード例 #15
0
		/** Lookup the 3D points for each pixel location */
        public Point3DList MapPoints(List<PointF> laserLocations, Bitmap image, Color defColor)
		{
			double MAX_DIST_Y = TableSize.Height * 2;
			double MAX_DIST_XZ_SQ = (TableSize.Width / 2) * (TableSize.Width / 2);

			Point3DList points = new Point3DList(laserLocations.Count);

			int numIntersectionFails = 0;
			int numDistanceFails = 0;

			Ray ray;
			bool haveImage = image != null;

			// Initialize our output variable
			for (int iLoc = 0; iLoc < laserLocations.Count; iLoc++)
			{
				// Compute the back projection ray
				ray = CalculateCameraRay(laserLocations[iLoc]);

				// Intersect the laser plane and populate the XYZ
				Point3D point = new Point3D();
				if (IntersectLaserPlane(ray, ref point, laserLocations[iLoc]))
				{
					// The point must be above the turn table and less than the max distance from the center of the turn table
					double distXZSq = point.Position.X * point.Position.X + point.Position.Z * point.Position.Z;

					if (point.Position.Y >= 0.0 && distXZSq < MAX_DIST_XZ_SQ && point.Position.Y < MAX_DIST_Y)
					{
						// Set the color
						if (haveImage)
						{
							point.Color = image.GetPixel(Utils.ROUND(laserLocations[iLoc].X), Utils.ROUND(laserLocations[iLoc].Y));
						}
						else
							point.Color = defColor;

						// Make sure we have the correct laser location
						laserLocations[points.Count] = laserLocations[iLoc];
						points.Add(point);
					}
					else
					{
						numDistanceFails++;
					}
				}
				else
				{
					numIntersectionFails++;
				}
			}

			if (numIntersectionFails > 0)
			{
				Debug.WriteLine("!! " + numIntersectionFails + " laser plane intersection failures.");
			}

			if (numDistanceFails > 0)
			{
				Debug.WriteLine("!! " + numDistanceFails + " object bounds failures. ");
			}
			return points;
		}
コード例 #16
0
		/** Calculate where the ray will hit the laser plane and write it to @p point */
        private bool IntersectLaserPlane(Ray ray, ref Point3D point, PointF pixel)
		{
			// Reference: http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-plane-and-ray-disk-intersection/
			// d = ((p0 - l0) * n) / (l * n)

			// If dn is close to 0 then they don't intersect.  This should never happen
			double denominator = ray.Direction.Dot(m_LaserPlane.Normal);
			if (Math.Abs(denominator) < 0.000001)
			{
				Debug.WriteLine("!!! Ray never hits laser plane, pixel=" + pixel.X + ", " + pixel.Y + ", laserX=" + m_LaserPos.X + ", denom=" + denominator);
				return false;
			}

			Vector3d v;
			v.X = m_LaserPlane.Point.X - ray.Origin.X;
			v.Y = m_LaserPlane.Point.Y - ray.Origin.Y;
			v.Z = m_LaserPlane.Point.Z - ray.Origin.Z;

			double numerator = v.Dot(m_LaserPlane.Normal);

			// Compute the distance along the ray to the plane
			double d = numerator / denominator;
			if (d < 0)
			{
				// The ray is going away from the plane.  This should never happen.
				Debug.WriteLine("!!! Back projection ray is going the wrong direction!  Ray Origin = (" +
											 ray.Origin.X + "," + ray.Origin.Y + "," + ray.Origin.Z + ") Direction = (" +
											 ray.Direction.X + "," + ray.Direction.Y + "," + ray.Direction.Z + ")");
				return false;
			}

			// Extend the ray out this distance
			point.Position.X = ray.Origin.X + (ray.Direction.X * d);
			point.Position.Y = ray.Origin.Y + (ray.Direction.Y * d);
			point.Position.Z = ray.Origin.Z + (ray.Direction.Z * d);
			point.Normal.X = m_LaserPos.X - point.Position.X;
			point.Normal.Y = m_LaserPos.Y - point.Position.Y;
			point.Normal.Z = m_LaserPos.Z - point.Position.Z;

			point.Normal.Normalize();

			return true;
		}
コード例 #17
0
		protected Point3D Smooth(Point3D p, IList<Point3D> nearPts)
		{
			if(nearPts.Count<1)
				return p;
			Vector3d pos = new Vector3d(0, 0, 0);
			Vector3d norm = new Vector3d(0, 0, 0);

			int count =nearPts.Count;
			for (int i = 0; i < count; i++)
			{
				pos = pos + (nearPts[i].Position - p.Position);
				norm = norm + (nearPts[i].Normal - p.Normal);
			}

			pos = p.Position + pos / count;
			norm = p.Normal + norm / count;
			norm.Normalize();

			return new Point3D(pos,norm,p.Color);
		}
コード例 #18
0
ファイル: Point3D.cs プロジェクト: robotsrulz/Sardauscan
		private static Point3D CalculateCenterOfGravity(this IList<Point3D> vertices)
		{
			Point3D centerOfGravity = new Point3D();


			foreach (Point3D vr in vertices)
				centerOfGravity.Position += vr.Position;
			centerOfGravity.Position /= (double)vertices.Count;
			return centerOfGravity;
		}
コード例 #19
0
ファイル: Triangle3D.cs プロジェクト: robotsrulz/Sardauscan
			/// <summary>
			/// Calculate a normal for 3 points
			/// </summary>
			/// <param name="a"></param>
			/// <param name="b"></param>
			/// <param name="c"></param>
			/// <returns></returns>
        public static Vector3d CalculateNormal(Point3D a, Point3D b, Point3D c)
        {
            return CalculateNormal(a.Position, b.Position, c.Position);
        }
コード例 #20
0
ファイル: Point3D.cs プロジェクト: robotsrulz/Sardauscan
		public static void AddVector(this IList<Point3D> vertices, Point3D newOrigin)
		{
			//reset vertex so that it starts from 0,0,0
			for (int i = 0; i < vertices.Count; i++)
			{
				Vector3d v = vertices[i].Position;
				v.X += newOrigin.Position.X;
				v.Y += newOrigin.Position.Y;
				v.Z += newOrigin.Position.Z;
				vertices[i].Position = v;

			}

		}
コード例 #21
0
ファイル: Triangle3D.cs プロジェクト: robotsrulz/Sardauscan
			/// <summary>
			/// Ctor
			/// </summary>
			/// <param name="p1"></param>
			/// <param name="p2"></param>
			/// <param name="p3"></param>
        public Triangle3D(Point3D p1, Point3D p2, Point3D p3)
            :this(p1,p2,p3,CalculateNormal(p1,p2,p3),CalculateColor(p1, p2, p3))
        {
        }
コード例 #22
0
ファイル: ScanDataIO.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary>
		/// Read ScanData File
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public static ScanData Read(string file)
		{
			ScanData ret = new ScanData();
			using (StreamReader r = System.IO.File.OpenText(file))
			{
				string line = r.ReadLine();
				string[] part = line.Split(":".ToArray());
				int slicecount = int.Parse(part[1]);
				for (int i = 0; i < slicecount; i++)
				{
					line = r.ReadLine();
					part = line.Split(":".ToArray());
					ScanLine slice = new ScanLine((int)double.Parse(part[1]));

					line = r.ReadLine();
					part = line.Split(":".ToArray());
					if (part[0] == "DrawAs")
					{
						OpenTK.Graphics.OpenGL.PrimitiveType primitive = OpenTK.Graphics.OpenGL.PrimitiveType.Points;
						Enum.TryParse<OpenTK.Graphics.OpenGL.PrimitiveType>(part[1], true, out primitive);
						switch (primitive)
						{
							case OpenTK.Graphics.OpenGL.PrimitiveType.TriangleStrip:
								{
									slice = new ScanSlice(10000);
									break;
								}
							case OpenTK.Graphics.OpenGL.PrimitiveType.LineStrip:
								{
									slice.DisplayAsLine = true;
									break;
								}
							default:
								{
									slice.DisplayAsLine = false;
									break;
								}
						}
						line = r.ReadLine();
						part = line.Split(":".ToArray());
					}
					int pointcount = int.Parse(part[1]);

					for (int j = 0; j < pointcount; j++)
					{
						line = r.ReadLine();
						part = line.Split("|".ToArray());

						Vector3d pos = GetVector(part[0]);
						Vector3d normal = pos.Normalized();
						try
						{
							normal = GetVector(part[1]);
						}
						catch { }
						Color color = System.Drawing.ColorTranslator.FromHtml(part[2]);
						Point3D p = new Point3D(pos, normal, color);
						slice.Add(p);
					}
					ret.Add(slice);
				}
			}
			return ret;
		}
コード例 #23
0
ファイル: Triangle3D.cs プロジェクト: robotsrulz/Sardauscan
			/// <summary>
			/// Ctor
			/// </summary>
			/// <param name="p1"></param>
			/// <param name="p2"></param>
			/// <param name="p3"></param>
			/// <param name="color"></param>
        public Triangle3D(Point3D p1, Point3D p2, Point3D p3, Color color)
            : this(p1, p2, p3, CalculateNormal(p1, p2, p3), color)
        {
        }
コード例 #24
0
ファイル: KDNode.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary> 
		/// Insert the point into the leaf. 
		/// </summary> 
		/// <param name="tPoint">The point to insert the data at.</param> 
		/// <param name="kValue">The value at the point.</param> 
		private void AddLeafPoint(Vector3d position,Point3D kValue)
		{
			// Add the data point to this node. 
			tPoints[Size] = position;
			tData[Size] = kValue;
			ExtendBounds(position);
			Size++;

			// Split if the node is getting too large in terms of data. 
			if (Size == tPoints.Length - 1)
			{
				// If the node is getting too physically large. 
				if (CalculateSplit())
				{
					// If the node successfully had it's split value calculated, split node. 
					SplitLeafNode();
				}
				else
				{
					// If the node could not be split, enlarge node data capacity. 
					IncreaseLeafCapacity();
				}
			}
		}
コード例 #25
0
ファイル: IntervalHeap.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary> 
		/// Swap out the item with the largest key in the queue. 
		/// </summary> 
		/// <param name="key">The new key for the largest item.</param> 
		/// <param name="value">The new data for the largest item.</param> 
		public void ReplaceMax(double key, Point3D value)
		{
			if (Size == 0)
			{
				throw new Exception();
			}
			else if (Size == 1)
			{
				ReplaceMin(key, value);
				return;
			}

			tData[1] = value;
			tKeys[1] = key;
			// Swap with pair if necessary 
			if (key < tKeys[0])
			{
				Swap(0, 1);
			}
			SiftDownMax(1);
		}
コード例 #26
0
ファイル: Triangle3D.cs プロジェクト: robotsrulz/Sardauscan
				/// <summary>
				/// Calculate a color based on 3 Points (average)
				/// </summary>
				/// <param name="p1"></param>
				/// <param name="p2"></param>
				/// <param name="p3"></param>
				/// <returns></returns>
				public static Color CalculateColor(Point3D p1, Point3D p2, Point3D p3)
        {
            return CalculateColor(p1.Color, p2.Color, p3.Color);
        }
コード例 #27
0
			/// <summary>
			/// Calculate a normal for 3 points
			/// </summary>
			/// <param name="a"></param>
			/// <param name="b"></param>
			/// <param name="c"></param>
			/// <returns></returns>
        public static Vector3d CalculateNormal(Point3D a, Point3D b, Point3D c)
        {
            return CalculateNormal(a.Position, b.Position, c.Position);
        }
コード例 #28
0
ファイル: Triangle3D.cs プロジェクト: robotsrulz/Sardauscan
			/// <summary>
			/// Ctor
			/// </summary>
			/// <param name="p1"></param>
			/// <param name="p2"></param>
			/// <param name="p3"></param>
			/// <param name="normal"></param>
        public Triangle3D(Point3D p1, Point3D p2, Point3D p3,Vector3d normal)
            : this(p1, p2, p3, normal, CalculateColor(p1, p2, p3))
        {
        }
コード例 #29
0
				/// <summary>
				/// Calculate a color based on 3 Points (average)
				/// </summary>
				/// <param name="p1"></param>
				/// <param name="p2"></param>
				/// <param name="p3"></param>
				/// <returns></returns>
				public static Color CalculateColor(Point3D p1, Point3D p2, Point3D p3)
        {
            return CalculateColor(p1.Color, p2.Color, p3.Color);
        }
コード例 #30
0
			/// <summary>
			/// Ctor
			/// </summary>
			/// <param name="p1"></param>
			/// <param name="p2"></param>
			/// <param name="p3"></param>
        public Triangle3D(Point3D p1, Point3D p2, Point3D p3)
            :this(p1,p2,p3,CalculateNormal(p1,p2,p3),CalculateColor(p1, p2, p3))
        {
        }
コード例 #31
0
ファイル: IntervalHeap.cs プロジェクト: robotsrulz/Sardauscan
		/// <summary> 
		/// Insert a new data item at a given key. 
		/// </summary> 
		/// <param name="key">The value which represents our data (i.e. a distance).</param> 
		/// <param name="value">The data we want to store.</param> 
		public void Insert(double key, Point3D value)
		{
			// If more room is needed, double the array size. 
			if (Size >= Capacity)
			{
				// Double the capacity. 
				Capacity *= 2;

				// Expand the data array. 
				var newData = new Point3D[Capacity];
				Array.Copy(tData, newData, tData.Length);
				tData = newData;

				// Expand the key array. 
				var newKeys = new double[Capacity];
				Array.Copy(tKeys, newKeys, tKeys.Length);
				tKeys = newKeys;
			}

			// Insert the new value at the end. 
			Size++;
			tData[Size - 1] = value;
			tKeys[Size - 1] = key;

			// Ensure it is in the right place. 
			SiftInsertedValueUp();
		}