/// <summary>
            /// Calculates position of the closest point on the line to the passed point
            /// </summary>
            /// <param name="point">Passed point that will be snapped</param>
            /// <param name="snappedPointType">Precalculated snapping type (from the distance method)</param>
            /// <returns>Position of closest point</returns>
            /// <exception cref="ArgumentOutOfRangeException">Invalid snapped point type</exception>
            public Vector3 ClosestPoint(Vector3 point, SnappedPointType snappedPointType)
            {
                switch (snappedPointType)
                {
                case SnappedPointType.InBetween:
                    var x = (b * (b * point.x - a * point.z) - a * c) / a2b2;
                    var z = (a * (-b * point.x + a * point.z) - b * c) / a2b2;
                    return(new Vector3(x, point.y, z));

                case SnappedPointType.Start:
                    return(new Vector3(start.x, point.y, start.z));

                case SnappedPointType.End:
                    return(new Vector3(end.x, point.y, end.z));

                default:
                    throw new ArgumentOutOfRangeException(nameof(snappedPointType), snappedPointType, null);
                }
            }
            /// <summary>
            /// Calculates distance to the line, returns float.MaxValue is greater than passed max distance
            /// </summary>
            /// <param name="point">Point that will be snapped to line</param>
            /// <param name="maxDistance">Maximal distance that can be applied</param>
            /// <param name="snappedPointType">Returns type how point will be snapped to the line</param>
            /// <returns>Distance between the point and the line, returns float.MaxValue is greater than passed max distance</returns>
            public float Distance(Vector3 point, float maxDistance, out SnappedPointType snappedPointType)
            {
                var distanceToStraightLine = Mathf.Abs((a * point.x + b * point.z + c) / sqrta2b2);

                if (distanceToStraightLine > maxDistance)
                {
                    snappedPointType = SnappedPointType.Invalid;
                    return(float.MaxValue);
                }

                var pointStart    = Distance(point, start);
                var pointEnd      = Distance(point, end);
                var lengthPow     = Mathf.Pow(length, 2);
                var pointStartPow = Mathf.Pow(pointStart, 2);
                var pointEndPow   = Mathf.Pow(pointEnd, 2);

                //Check if point will be snapped to the start, end or in between
                //Point is snapped to point between start and end only when the formed triangle is obtuse
                if (pointStartPow > lengthPow + pointEndPow)
                {
                    if (pointEnd > maxDistance)
                    {
                        snappedPointType = SnappedPointType.Invalid;
                        return(float.MaxValue);
                    }
                    snappedPointType = SnappedPointType.End;
                    return(pointEnd);
                }

                if (pointEndPow > lengthPow + pointStartPow)
                {
                    if (pointStart > maxDistance)
                    {
                        snappedPointType = SnappedPointType.Invalid;
                        return(float.MaxValue);
                    }
                    snappedPointType = SnappedPointType.Start;
                    return(pointStart);
                }

                snappedPointType = SnappedPointType.InBetween;
                return(distanceToStraightLine);
            }