private void OnDeleteClick()
        {
            var selected = EventSystem.current.currentSelectedGameObject;

            if (selected && selected.TryGetComponent(out Button _))
            {
                string btnName = selected.name;
                if (int.TryParse(btnName.Substring(btnName.Length - 1), out int i))
                {
                    if (deletedIndex != -1)
                    {
                        CVSPUIManager.PostMessage("#LOC_CVSP_WRN_AtLeastThreeVertices");
                        vertexInfos[deletedIndex].EnableUIElements();
                    }
                    deletedIndex = i;
                    vertexInfos[i].DisableUIElements();
                    Vector3 pos = deletedHighlight.transform.position;
                    deletedHighlight.transform.position = new Vector3(pos.x, vertexInfos[i].parentObject.position.y, pos.z);
                    deletedHighlight.SetActive(true);
                }
            }
        }
        private void OnConfirm()
        {
            if (pickingVertex)
            {
                FinishPick();
            }
            else
            {
                int     deletedIndexTemp = deletedIndex;
                Vector3 v0, v1, v2, v3 = Vector3.zero;
                //Main axis (part local Y axis)
                Vector3    mainAxis;
                Vector3    position;
                Vector3    zAxis;
                Quaternion orientation;
                float      height0, height1;
                float      length;
                float      tilt0;
                float      tilt1;
                Vector3    normal;
                if (!float.TryParse(thicknessInput.text, out float thickness))
                {
                    thickness = 0;
                }
                float width = thickness;

                #region Check if there are vertices at the same position
                bool hasOverlap = false;
                for (int i = 0; i < vertexInfos.Length; i++)
                {
                    if (i != deletedIndexTemp)
                    {
                        var v = vertexInfos[i].currentCoords;
                        for (int j = i + 1; j < vertexInfos.Length - i; j++)
                        {
                            if (j != deletedIndexTemp && vertexInfos[j].currentCoords == v)
                            {
                                if (!hasOverlap)
                                {
                                    deletedIndexTemp = j;
                                    hasOverlap       = true;
                                }
                                else
                                {
                                    CVSPUIManager.PostMessage("#LOC_CVSP_WRN_AtLeastThreeVertices");
                                    return;
                                }
                            }
                        }
                    }
                }
                #endregion

                Vector3 v01, v32;
                #region Re-organize vertices order to make sure thickness is applied on correct side

                #region Calculate normal
                if (deletedIndexTemp < 0)
                {
                    v0  = vertexInfos[0].currentCoords;
                    v1  = vertexInfos[1].currentCoords;
                    v2  = vertexInfos[2].currentCoords;
                    v3  = vertexInfos[3].currentCoords;
                    v01 = v1 - v0;
                    v32 = v2 - v3;
                    var norm012 = Vector3.Cross(v01, v2 - v1);
                    var norm230 = Vector3.Cross(-v32, v0 - v3);

                    #region Fix abnormal normals
                    if (Vector3.Dot(norm012, norm230) < 0)
                    {
                        var temp = v3;
                        v3      = v2;
                        v2      = temp;
                        v32     = v2 - v3;
                        norm230 = Vector3.Cross(-v32, v0 - v3);
                    }
                    #endregion

                    normal = (norm012 + norm230) / 2;
                }
                else
                {
                    int i = 0;
                    if (i == deletedIndexTemp)
                    {
                        i++;
                    }
                    v0 = vertexInfos[i++].currentCoords;
                    if (i == deletedIndexTemp)
                    {
                        i++;
                    }
                    v1 = vertexInfos[i++].currentCoords;
                    if (i == deletedIndexTemp)
                    {
                        i++;
                    }
                    v2     = vertexInfos[i].currentCoords;
                    v01    = v1 - v0;
                    normal = Vector3.Cross(v01, v2 - v1);
                }
                #endregion

                #region Flip
                if (Vector3.Dot(CapsuleRay.editorCamera.transform.forward, normal) > 0)
                {
                    normal = -normal;
                    var temp = v1;
                    v1 = v0;
                    v0 = temp;
                    if (deletedIndexTemp < 0)
                    {
                        temp = v3;
                        v3   = v2;
                        v2   = temp;
                    }
                }
                #endregion
                #endregion

                var   offsetDistance = thickness / 2;
                float twist          = 0;

                if (deletedIndexTemp < 0)
                {
                    v01 = v1 - v0;
                    v32 = v2 - v3;

                    #region Apply thickness (by offseting vertices)
                    var     norm012   = Vector3.Cross(v01, v2 - v1).normalized;
                    var     norm230   = Vector3.Cross(-v32, v0 - v3).normalized;
                    Vector3 offset012 = -norm012 * offsetDistance;
                    v0 += offset012;
                    v1 += offset012;
                    Vector3 offset230 = -norm230 * offsetDistance;
                    v2 += offset230;
                    v3 += offset230;
                    v01 = v1 - v0;
                    v32 = v2 - v3;
                    #endregion

                    var mid01 = (v0 + v1) / 2;
                    var mid32 = (v2 + v3) / 2;
                    mainAxis = mid32 - mid01;
                    position = (mid01 + mid32) / 2;
                    Vector3 v01prj = v01 - Vector3.Project(v01, mainAxis);
                    Vector3 v32prj = v32 - Vector3.Project(v32, mainAxis);
                    zAxis = v01prj;

                    #region Clamp twist to +/-45 deg
                    twist = -Vector3.SignedAngle(v01prj, v32prj, mainAxis);
                    if (Mathf.Abs(twist) > 45f)
                    {
                        var limit = Mathf.Sign(twist) * 45f;
                        CVSPUIManager.PostMessage($"#LOC_CVSP_Twist {twist.ToString("#0.#")} #LOC_CVSP_OutOfLimit +/-45");
                        var correction = limit - twist;
                        twist = limit;
                        //减去偏移,以免它造成旋转后位置不正确
                        Quaternion q = Quaternion.AngleAxis(correction, mainAxis);
                        v2       -= offset230;
                        v3       -= offset230;
                        v2        = q * v2;
                        v3        = q * v3;
                        offset230 = q * offset230;
                        v2       += offset230;
                        v3       += offset230;
                        vertexInfos[2].SetCoordinates(v2);
                        vertexInfos[3].SetCoordinates(v3);
                        vertexInfos[2].UpdateInputField();
                        vertexInfos[3].UpdateInputField();
                    }
                    #endregion

                    #region Calculate shape
                    tilt0   = Vector3.SignedAngle(v01, mainAxis, Vector3.Cross(v01, mainAxis)) + -90f;
                    tilt1   = Vector3.SignedAngle(v32, mainAxis, Vector3.Cross(v32, mainAxis)) + -90f;
                    height0 = v01.magnitude;
                    height1 = v32.magnitude;
                    #endregion
                }
                else
                {
                    v01 = v1 - v0;

                    #region Apply thickness (by offseting vertices)
                    var norm012 = Vector3.Cross(v01, v2 - v1).normalized;
                    v0 -= norm012 * offsetDistance;
                    v1 -= norm012 * offsetDistance;
                    v2 -= norm012 * offsetDistance;
                    #endregion

                    var mid01 = (v0 + v1) / 2;
                    mainAxis = v2 - mid01;
                    Vector3 v01prj = v01 - Vector3.Project(v01, mainAxis);
                    zAxis = v01prj;

                    #region Calculate shape
                    position = (mid01 + v2) / 2;
                    height0  = v01.magnitude;
                    height1  = 0;
                    tilt0    = Vector3.SignedAngle(v01, mainAxis, Vector3.Cross(v01, mainAxis)) + -90f;
                    tilt1    = 0;
                    #endregion
                }

                length      = mainAxis.magnitude;
                orientation = Quaternion.LookRotation(-zAxis, -mainAxis);
                if (Mathf.Abs(tilt0) > 45f)
                {
                    CVSPUIManager.PostMessage($"#LOC_CVSP_Tilt {tilt0.ToString("#0.#")} #LOC_CVSP_OutOfLimit #LOC_CVSP_NeedHelp");
                }
                if (Mathf.Abs(tilt1) > 45f)
                {
                    CVSPUIManager.PostMessage($"#LOC_CVSP_Tilt {tilt1.ToString("#0.#")} #LOC_CVSP_OutOfLimit #LOC_CVSP_NeedHelp");
                }
                if (height0 > 20f)
                {
                    CVSPUIManager.PostMessage($"#LOC_CVSP_Height 0 {height0.ToString("#0.#")} #LOC_CVSP_OutOfLimit #LOC_CVSP_NeedHelp");
                }
                if (height1 > 20f)
                {
                    CVSPUIManager.PostMessage($"#LOC_CVSP_Height 1 {height1.ToString("#0.#")} #LOC_CVSP_OutOfLimit #LOC_CVSP_NeedHelp");
                }
                if (length > 20f)
                {
                    CVSPUIManager.PostMessage($"#LOC_CVSP_Length {length.ToString("#0.#")} #LOC_CVSP_OutOfLimit #LOC_CVSP_NeedHelp");
                }

                #region Send Parameters to create part
                CVSPPartInfo info = new CVSPPartInfo()
                {
                    height0     = height0,
                    height1     = height1,
                    width       = width,
                    length      = length,
                    position    = position,
                    orientation = orientation,
                    twist       = twist,
                    tilt0       = tilt0,
                    tilt1       = tilt1
                };
                CVSPUIManager.CreateCVSPPart(info);
                #endregion
            }
        }