private Circle3 SolveTopCircle()
    {
        // Camera
        Vector3 eyePos = m_projector.m_mainCamera.transform.position;

        // Step 1: Find Long Axis and short Axis
        // long Axis: majorendp majorstartp
        // Short Axis: minorendp minorstartp

        // Step 2: Set radius, Center & two point on Long Axis
        float   dist_c = 10;
        Vector2 c_2d   = (majorstartp + majorendp) / 2;
        Vector3 c      = eyePos + dist_c / m_projector.GenerateRay(c_2d).direction.z *m_projector.GenerateRay(c_2d).direction;

        majorstartp3 = eyePos + dist_c / m_projector.GenerateRay(majorstartp).direction.z *m_projector.GenerateRay(majorstartp).direction;
        majorendp3   = eyePos + dist_c / m_projector.GenerateRay(majorendp).direction.z *m_projector.GenerateRay(majorendp).direction;
        float radius = Vector3.Distance(majorstartp3, majorendp3) / 2;

        // Step 3: Get Project Ray from end point of short Axis
        minorstartp3 = IExtension.RayHitShpere(eyePos, m_projector.GenerateRay(minorstartp).direction, c, (double)radius);

        // Step 4: Get one from two results
        // chenxin      to be improved
        // Step 5: Use 3 points to get Circle Plane, then this Circle


        return(new Circle3(c, radius, new Plane(majorstartp3, majorendp3, minorstartp3), boundary2.Count));
    }
    public void Optimize_allRects()
    {
        // Init FOV
        float fov_aver = 0;

        foreach (var fe in m_FEs)
        {
            fov_aver = fe.fov_face;
        }
        fov_aver /= m_FEs.Count;
        m_engine.m_fovOptimizer.SetFOV(fov_aver);
        float fov_init = m_projector.m_mainCamera.fieldOfView;

        // Count rect engines
        foreach (var fe in m_FEs)
        {
            if (!fe.isTopCircle_Current)
            {
                REs.Add(fe);
            }
        }

        // Params Prepare
        //foreach (var re in rectEngines) corners_list.Add(re.corner_points);
        int params_num = 4 * REs.Count + 1;

        double[] bndl = new double[params_num];
        double[] x    = new double[params_num];
        double[] bndu = new double[params_num];
        for (int i = 0; i < REs.Count; i++)
        {
            bndl[4 * i]     = 0;
            bndl[4 * i + 1] = 0;
            bndl[4 * i + 2] = 0;
            bndl[4 * i + 3] = 0;
            x[4 * i]        = REs[i].x0;
            x[4 * i + 1]    = REs[i].x1;
            x[4 * i + 2]    = REs[i].x2;
            x[4 * i + 3]    = REs[i].x3;
            bndu[4 * i]     = 20;
            bndu[4 * i + 1] = 20;
            bndu[4 * i + 2] = 20;
            bndu[4 * i + 3] = 20;
        }
        bndl[params_num - 1] = 1 / fov_optimal_scale;
        x[params_num - 1]    = fov_aver / fov_optimal_scale;
        bndu[params_num - 1] = 179 / fov_optimal_scale;

        int funNum = 11;

        double diffstep = 0.0001;
        double epsg     = 0.000000000001;
        double epsf     = 0;
        double epsx     = 0;
        int    maxits   = 0;

        alglib.minlmstate  state;
        alglib.minlmreport rep;

        //set timer
        System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
        stopwatch.Start();
        Inter_Num = 0;
        alglib.minlmcreatev(funNum, x, diffstep, out state);
        alglib.minlmsetbc(state, bndl, bndu);
        alglib.minlmsetcond(state, epsg, epsf, epsx, maxits);
        alglib.minlmoptimize(state, function_multi, null, null);
        alglib.minlmresults(state, out x, out rep);


        stopwatch.Stop();
        if (!m_engine.m_is_quiet)
        {
            Debug.Log("End fitting , Total time:" + stopwatch.ElapsedMilliseconds / 1000.0 + "s");
        }

        m_engine.m_fovOptimizer.SetFOV((float)x[x.Length - 1] * fov_optimal_scale);
        for (int i = 0; i < REs.Count; i++)
        {
            Ray ray1 = m_projector.GenerateRay(REs[i].corner_points[0]);
            Ray ray2 = m_projector.GenerateRay(REs[i].corner_points[1]);
            Ray ray3 = m_projector.GenerateRay(REs[i].corner_points[2]);
            Ray ray4 = m_projector.GenerateRay(REs[i].corner_points[3]);

            Vector3 v1_3 = ray1.GetPoint((float)x[4 * i]);
            Vector3 v2_3 = ray2.GetPoint((float)x[4 * i + 1]);
            Vector3 v3_3 = ray3.GetPoint((float)x[4 * i + 2]);
            Vector3 v4_3 = ray4.GetPoint((float)x[4 * i + 3]);
            // project to a new plane
            List <Vector3> points = new List <Vector3>();
            points.Add(v1_3);
            points.Add(v2_3);
            points.Add(v3_3);
            points.Add(v4_3);
            Vector3 mean = v1_3 + v2_3 + v3_3 + v4_3;
            mean /= 4;
            Vector3 normal   = Utility.GetNormal(points);
            Plane   newplane = new Plane(normal, mean);
            v1_3           = Utility.PlaneProjectPoint(newplane, v1_3);
            v2_3           = Utility.PlaneProjectPoint(newplane, v2_3);
            v3_3           = Utility.PlaneProjectPoint(newplane, v3_3);
            v4_3           = Utility.PlaneProjectPoint(newplane, v4_3);
            REs[i].topRect = new Quad(v1_3, v2_3, v3_3);
            REs[i].topRect.FitRect();
            m_renderEngine.DrawRect3(REs[i].topRect, Color.green);
            m_renderEngine.DrawLine(REs[i].topRect.Center, REs[i].topRect.Center + 2 * REs[i].topRect.Normal, Color.blue);
            if (!m_engine.m_is_quiet)
            {
                Debug.Log("angle: " + Vector3.Angle(REs[i].topRect.CornerPoints3d[0] - REs[i].topRect.CornerPoints3d[1], REs[i].topRect.CornerPoints3d[2] - REs[i].topRect.CornerPoints3d[1]));
            }
        }
    }