public static float ConvertToSh3DArcAngle(Vector3 arcOrigin, Vector3 arcStartPoint, Vector3 arcPlaneNormal, float degreesFromStart)
        {
            degreesFromStart %= 360.0f;
            if (Mathf.Abs(degreesFromStart) > 180.0f)
            {
                Vector3 toStartPt = (arcStartPoint - arcOrigin);
                Vector3 toEndPt   = (Quaternion.AngleAxis(degreesFromStart, arcPlaneNormal) * toStartPt).normalized;
                degreesFromStart = Vector3Ex.SignedAngle(toStartPt, toEndPt, arcPlaneNormal);
            }

            return(degreesFromStart);
        }
        public static bool Is3DPointOnLgArcWire(Vector3 point, bool checkOnPlane, Vector3 arcOrigin, Vector3 arcStartPoint,
                                                Vector3 arcPlaneNormal, float degreesFromStart, ArcEpsilon epsilon = new ArcEpsilon())
        {
            if (Mathf.Abs(degreesFromStart) <= 180.0f)
            {
                return(Is3DPointOnShArcWire(point, checkOnPlane, arcOrigin, arcStartPoint, arcPlaneNormal, degreesFromStart, epsilon));
            }

            Vector3 toStartPt = (arcStartPoint - arcOrigin);
            Vector3 toPt      = (point - arcOrigin);

            float distToPt  = toPt.magnitude;
            float arcRadius = (arcOrigin - arcStartPoint).magnitude;

            Plane arcPlane = new Plane(arcPlaneNormal, arcOrigin);

            if (checkOnPlane && arcPlane.GetAbsDistanceToPoint(point) > epsilon.ExtrudeEps)
            {
                return(false);
            }

            Vector3 arcEndPoint         = Quaternion.AngleAxis(degreesFromStart, arcPlaneNormal) * toStartPt + arcOrigin;
            float   distanceFromSegment = point.GetDistanceToSegment(arcOrigin, arcStartPoint);

            if (distanceFromSegment <= epsilon.WireEps)
            {
                return(true);
            }

            distanceFromSegment = point.GetDistanceToSegment(arcOrigin, arcEndPoint);
            if (distanceFromSegment <= epsilon.WireEps)
            {
                return(true);
            }

            degreesFromStart = ConvertToSh3DArcAngle(arcOrigin, arcStartPoint, arcPlaneNormal, degreesFromStart);
            float startToPtAngle = Vector3Ex.SignedAngle(toStartPt, toPt, arcPlaneNormal);

            if (Mathf.Sign(startToPtAngle) == Mathf.Sign(degreesFromStart) &&
                Mathf.Abs(startToPtAngle) <= Mathf.Abs(degreesFromStart))
            {
                return(false);
            }

            return(distToPt >= arcRadius - epsilon.WireEps &&
                   distToPt <= arcRadius + epsilon.WireEps);
        }
        public static bool ShArcContains3DPoint(Vector3 point, bool checkOnPlane, Vector3 arcOrigin, Vector3 arcStartPoint,
                                                Vector3 arcPlaneNormal, float degreesFromStart, ArcEpsilon epsilon = new ArcEpsilon())
        {
            Vector3 toStartPt = (arcStartPoint - arcOrigin);
            Vector3 toPt      = (point - arcOrigin);

            float arcRadius = (arcOrigin - arcStartPoint).magnitude + epsilon.AreaEps;

            if (arcRadius < toPt.magnitude)
            {
                return(false);
            }

            Plane arcPlane = new Plane(arcPlaneNormal, arcOrigin);

            if (checkOnPlane && arcPlane.GetAbsDistanceToPoint(point) > epsilon.ExtrudeEps)
            {
                return(false);
            }

            float startToPtAngle = Vector3Ex.SignedAngle(toStartPt, toPt, arcPlaneNormal);

            if (Mathf.Sign(startToPtAngle) == Mathf.Sign(degreesFromStart) &&
                Mathf.Abs(startToPtAngle) <= Mathf.Abs(degreesFromStart))
            {
                return(true);
            }

            if (epsilon.AreaEps != 0.0f)
            {
                Vector3 arcEndPoint         = Quaternion.AngleAxis(degreesFromStart, arcPlaneNormal) * toStartPt + arcOrigin;
                float   distanceFromSegment = point.GetDistanceToSegment(arcOrigin, arcStartPoint);
                if (distanceFromSegment <= epsilon.AreaEps)
                {
                    return(true);
                }

                distanceFromSegment = point.GetDistanceToSegment(arcOrigin, arcEndPoint);
                if (distanceFromSegment <= epsilon.AreaEps)
                {
                    return(true);
                }
            }

            return(false);
        }
        public static bool LgArcContains3DPoint(Vector3 point, bool checkOnPlane, Vector3 arcOrigin, Vector3 arcStartPoint,
                                                Vector3 arcPlaneNormal, float degreesFromStart, ArcEpsilon epsilon = new ArcEpsilon())
        {
            degreesFromStart %= 360.0f;
            if (Mathf.Abs(degreesFromStart) <= 180.0f)
            {
                return(ShArcContains3DPoint(point, checkOnPlane, arcOrigin, arcStartPoint, arcPlaneNormal, degreesFromStart, epsilon));
            }

            Vector3 toPt      = (point - arcOrigin);
            Vector3 toStartPt = (arcStartPoint - arcOrigin);

            degreesFromStart = ConvertToSh3DArcAngle(arcOrigin, arcStartPoint, arcPlaneNormal, degreesFromStart);
            float startToPtAngle      = Vector3Ex.SignedAngle(toStartPt, toPt, arcPlaneNormal);
            bool  isInsideShortestArc = Mathf.Sign(startToPtAngle) == Mathf.Sign(degreesFromStart) &&
                                        Mathf.Abs(startToPtAngle) <= Mathf.Abs(degreesFromStart);

            if (isInsideShortestArc && epsilon.AreaEps != 0.0f)
            {
                Vector3 arcEndPoint         = Quaternion.AngleAxis(degreesFromStart, arcPlaneNormal) * toStartPt + arcOrigin;
                float   distanceFromSegment = point.GetDistanceToSegment(arcOrigin, arcStartPoint);
                if (distanceFromSegment <= epsilon.AreaEps)
                {
                    return(true);
                }

                distanceFromSegment = point.GetDistanceToSegment(arcOrigin, arcEndPoint);
                if (distanceFromSegment <= epsilon.AreaEps)
                {
                    return(true);
                }

                return(false);
            }

            float arcRadius = (arcOrigin - arcStartPoint).magnitude + epsilon.AreaEps;

            return(toPt.magnitude <= arcRadius);
        }
        public static Quaternion Align(this Transform transform, Vector3 normAlignVector, TransformAxis alignmentAxis)
        {
            Vector3 axis = transform.up;

            if (alignmentAxis != TransformAxis.PositiveY)
            {
                if (alignmentAxis == TransformAxis.PositiveX)
                {
                    axis = transform.right;
                }
                else if (alignmentAxis == TransformAxis.NegativeX)
                {
                    axis = -transform.right;
                }
                else if (alignmentAxis == TransformAxis.NegativeY)
                {
                    axis = -transform.up;
                }
                else if (alignmentAxis == TransformAxis.PositiveZ)
                {
                    axis = transform.forward;
                }
                else if (alignmentAxis == TransformAxis.NegativeZ)
                {
                    axis = -transform.forward;
                }
            }

            float alignment = Vector3.Dot(axis, normAlignVector);

            if (1.0f - alignment < 1e-5f)
            {
                return(Quaternion.identity);
            }

            Vector3 rotAxis = Vector3.zero;

            // Check if the alignment axis is aligned with the alignment vector in the opposite direction
            if (alignment + 1.0f < 1e-5f)
            {
                if (alignmentAxis == TransformAxis.PositiveX)
                {
                    rotAxis = transform.up;
                }
                else if (alignmentAxis == TransformAxis.NegativeX)
                {
                    rotAxis = -transform.up;
                }
                else if (alignmentAxis == TransformAxis.PositiveY)
                {
                    rotAxis = transform.right;
                }
                else if (alignmentAxis == TransformAxis.NegativeY)
                {
                    rotAxis = -transform.right;
                }
                else if (alignmentAxis == TransformAxis.PositiveZ)
                {
                    rotAxis = transform.up;
                }
                else if (alignmentAxis == TransformAxis.NegativeZ)
                {
                    rotAxis = -transform.up;
                }
            }
            else
            {
                rotAxis = Vector3.Normalize(Vector3.Cross(axis, normAlignVector));
            }

            float rotAngle = Vector3Ex.SignedAngle(axis, normAlignVector, rotAxis);

            transform.Rotate(rotAxis, rotAngle, Space.World);
            return(Quaternion.AngleAxis(rotAngle, rotAxis));
        }