private KDTreePoint3D Helper_CreateTree(IList <Point3D> pointsTarget)
        {
            KDTreePoint3D kdTree = new KDTreePoint3D();

            kdTree.AddPoint(pointsTarget);
            return(kdTree);
        }
        public Matrix4d PerformICP()
        {
            IList <Point3D> PT       = PTarget.Copy();
            IList <Point3D> PS       = PSource.Copy();
            Point3D         pSOrigin = null;
            Point3D         pTOrigin = null;

            if (ResetPoint3DToOrigin)
            {
                pTOrigin = PT.ResetToOrigin();
                pSOrigin = PS.ResetToOrigin();
            }

            int keepOnlyPoints = 0;

            if (DistanceOptimization)
            {
                keepOnlyPoints = 3;
            }
            int iter = 0;

            try
            {
                if (!CheckSourceTarget(PT, PS))
                {
                    return(Matrix4d.Identity);
                }

                IList <Point3D> pointsTarget = PT.Copy();
                IList <Point3D> pointsSource = PS.Copy();

                this.Matrix = Matrix4d.Identity;
                Matrix4d bestMatrix       = Matrix4d.Mult(this.Matrix, 1f);
                double   bestMeanDistance = (double)(pointsTarget.CalculateTotalDistance(pointsSource) / Convert.ToDouble(pointsTarget.Count));
                double   oldMeanDistance  = 0;

                KDTreePoint3D kdTreee = Helper_CreateTree(pointsTarget);

                for (iter = 0; iter < MaximumNumberOfIterations; iter++)
                {
                    if (SimulatedAnnealing)
                    {
                        if (Helper_ICP_Iteration_SA(PT, PS, kdTreee, keepOnlyPoints))
                        {
                            break;
                        }
                    }
                    else
                    {
                        if (Helper_ICP_Iteration(ref pointsTarget, ref pointsSource, PT, PS, kdTreee, keepOnlyPoints))
                        {
                            break;
                        }
                    }
                    oldMeanDistance = MeanDistance;
                    Debug.WriteLine("--------------Iteration: " + iter.ToString() + " : Mean Distance: " + MeanDistance.ToString("0.00000000000"));
                    if (bestMeanDistance > MeanDistance)
                    {
                        bestMeanDistance = MeanDistance;
                        bestMatrix       = Matrix4d.Mult(this.Matrix, 1f);
                    }
                    else
                    {
                        return(bestMatrix);
                    }
                    Debug.WriteLine("--------------Iteration: " + iter.ToString() + " : BEST: " + bestMeanDistance.ToString("0.00000000000"));
                }

                Debug.WriteLine("--------****** Solution of ICP after : " + iter.ToString() + " iterations, and Mean Distance: " + MeanDistance.ToString("0.00000000000"));
                return(bestMatrix);
                //PTransformed = MathUtils.TransformPoints(PS, Matrix);
                ////re-reset vector
                //if (ResetPoint3DToOrigin)
                //{
                //  PTransformed.AddVector(pTOrigin);
                //  //Vertices.AddVector(PSource, pSOrigin);

                //}

                ////DebugWriteUtils.WriteTestOutputPoint3D("Solution of ICP", Matrix, this.PSource, PTransformed, PTarget);

                //return PTransformed;
            }
            catch (Exception err)
            {
                System.Windows.Forms.MessageBox.Show("Error in Update ICP at iteration: " + iter.ToString() + " : " + err.Message);
                return(Matrix4d.Identity);
            }
        }
        private bool Helper_ICP_Iteration_SA(IList <Point3D> PT, IList <Point3D> PS, KDTreePoint3D kdTree, int keepOnlyPoints)
        {
            try
            {
                //first iteration
                if (solutionList == null)
                {
                    solutionList = new List <ICPSolution>();


                    if (NumberOfStartTrialPoints > PS.Count)
                    {
                        NumberOfStartTrialPoints = PS.Count;
                    }
                    if (NumberOfStartTrialPoints == PS.Count)
                    {
                        NumberOfStartTrialPoints = PS.Count * 80 / 100;
                    }
                    if (NumberOfStartTrialPoints < 3)
                    {
                        NumberOfStartTrialPoints = 3;
                    }



                    for (int i = 0; i < MaxNumberSolutions; i++)
                    {
                        ICPSolution myTrial = ICPSolution.SetRandomIndices(NumberOfStartTrialPoints, PS.Count, solutionList);

                        if (myTrial != null)
                        {
                            myTrial.PointsSource = ExtractPoints(PS, myTrial.RandomIndices);
                            solutionList.Add(myTrial);
                        }
                    }
                    ////test....
                    ////maxNumberSolutions = 1;
                    //ICPSolution myTrial1 = new ICPSolution();
                    //for (int i = 0; i < NumberPointsSolution; i++)
                    //{
                    //    myTrial1.RandomIndices.Add(i);
                    //}
                    //myTrial1.PointsSource = RandomUtils.ExtractPoints(PS, myTrial1.RandomIndices);
                    //solutionList[0] = myTrial1;
                }


                for (int i = 0; i < solutionList.Count; i++)
                {
                    IList <Point3D> transformedPoints = null;

                    ICPSolution myTrial = solutionList[i];
                    Helper_ICP_Iteration(ref myTrial.PointsTarget, ref myTrial.PointsSource, PT, PS, kdTree, keepOnlyPoints);
                    myTrial.Matrix             = Matrix4d.Mult(myTrial.Matrix, this.Matrix);
                    myTrial.MeanDistanceSubset = this.MeanDistance;

                    myTrial.MeanDistance = TransformPoints(ref transformedPoints, PT, PS, myTrial.Matrix);

                    // solutionList[i] = myTrial;
                }
                if (solutionList.Count > 0)
                {
                    solutionList.Sort(new ICPSolutionComparer());
                    RemoveSolutionIfMatrixContainsNaN(solutionList);
                    if (solutionList.Count == 0)
                    {
                        System.Windows.Forms.MessageBox.Show("No solution could be found !");
                    }

                    this.Matrix       = solutionList[0].Matrix;
                    this.MeanDistance = solutionList[0].MeanDistance;

                    if (solutionList[0].MeanDistance < this.MaximumMeanDistance)
                    {
                        return(true);
                    }
                }
            }
            catch (Exception err)
            {
                MessageBox.Show("Error in Helper_ICP_Iteration_SA: " + err.Message);
                return(false);
            }

            return(false);
        }
        /// <summary>
        /// A single ICP Iteration
        /// </summary>
        /// <param name="pointsTarget"></param>
        /// <param name="pointsSource"></param>
        /// <param name="PT"></param>
        /// <param name="PS"></param>
        /// <param name="kdTree"></param>
        /// <returns></returns>
        private bool Helper_ICP_Iteration(ref IList <Point3D> pointsTarget, ref IList <Point3D> pointsSource, IList <Point3D> PT, IList <Point3D> PS, KDTreePoint3D kdTree, int keepOnlyPoints)
        {
            if (!Helper_FindNeighbours(ref pointsTarget, ref pointsSource, kdTree, keepOnlyPoints))
            {
                return(true);
            }

            Matrix4d        myMatrix            = Helper_FindTransformationMatrix(pointsTarget, pointsSource);
            IList <Point3D> myPointsTransformed = MathUtils.TransformPoints(pointsSource, myMatrix);

            if (SimulatedAnnealing)
            {
                this.Matrix = myMatrix;

                double totaldist = pointsTarget.CalculateTotalDistance(myPointsTransformed);
                this.MeanDistance = (double)(totaldist / Convert.ToDouble(pointsTarget.Count));

                //new set:
                pointsSource = myPointsTransformed;
                pointsTarget = PT.Copy();
            }
            else
            {
                Matrix4d.Mult(ref myMatrix, ref this.Matrix, out this.Matrix);
                double totaldist = pointsTarget.CalculateTotalDistance(myPointsTransformed);
                this.MeanDistance = (double)(totaldist / Convert.ToDouble(pointsTarget.Count));
                //Debug.WriteLine("--------------Iteration: " + iter.ToString() + " : Mean Distance: " + MeanDistance.ToString("0.00000000000"));

                if (MeanDistance < this.MaximumMeanDistance)                 //< Math.Abs(MeanDistance - oldMeanDistance) < this.MaximumMeanDistance)
                {
                    return(true);
                }

                Helper_SetNewInterationSets(ref pointsTarget, ref pointsSource, PT, PS);
            }
            return(false);
        }
 private bool Helper_FindNeighbours(ref IList <Point3D> pointsTarget, ref IList <Point3D> pointsSource, KDTreePoint3D kdTree, int keepOnlyPoints)
 {
     if (!FixedTestPoints)
     {
         pointsTarget = kdTree.FindNearest(pointsSource, pointsTarget, keepOnlyPoints);
         if (pointsTarget.Count != pointsSource.Count)
         {
             MessageBox.Show("Error finding neighbours, found " + pointsTarget.Count.ToString() + " out of " + pointsSource.Count.ToString());
             return(false);
         }
     }
     else
     {
         //adjust number of points - for the case if there are outliers
         int min = pointsSource.Count;
         if (pointsTarget.Count < min)
         {
             min = pointsTarget.Count;
             pointsSource.RemoveRange(pointsTarget.Count, pointsSource.Count - min);
         }
         else
         {
             pointsTarget.RemoveRange(pointsSource.Count, pointsTarget.Count - min);
         }
     }
     return(true);
 }