Skip to content

Helper function to find a perpendicular vector to a given vector This is a generic implementation that works with any vector type

julia
function find_orthogonal(v::AbstractVector)

Choose the smallest component to zero out

julia
    x, y, z = v[1], v[2], v[3]
    ax, ay, az = abs(x), abs(y), abs(z)

    if ax <= ay && ax <= az

x is smallest, zero it out

julia
        return UnitSphericalPoint(0.0, -z, y)
    elseif ay <= ax && ay <= az

y is smallest, zero it out

julia
        return UnitSphericalPoint(-z, 0.0, x)
    else

z is smallest, zero it out

julia
        return UnitSphericalPoint(-y, x, 0.0)
    end
end

"""
    isUnitLength(v::AbstractVector)

Check if a vector has unit length within a small tolerance.

Returns true if the vector's magnitude is approximately 1.0.
"""
function isUnitLength(v::AbstractVector)
    return isapprox(sum(abs2, v), 1.0, rtol=1e-14)
end

"""
    isNormalizable(v::AbstractVector)

Returns true if the given vector's magnitude is large enough such that
the angle to another vector of the same magnitude can be measured using
angle calculations without loss of precision due to floating-point underflow.

This matches S2's IsNormalizable function.
"""
function isNormalizable(v::AbstractVector)

Same threshold as in S2 - the largest component should be at least 2^-242 This ensures we can normalize without precision loss

julia
    return maximum(abs.(v)) >= ldexp(1.0, -242)
end

"""
    normalization_needed(v::AbstractVector)

Determines if a vector's magnitude is too small for reliable normalization.
Returns true if the vector needs special handling to avoid numerical issues.

This is essentially the opposite of isNormalizable.
"""
function normalization_needed(v::AbstractVector)

The threshold is 1e-14 squared, approximately 1e-28

julia
    norm_v² = sum(abs2, v)
    return norm_v² < 1e-28
end

"""
    ensureNormalizable(p::AbstractVector)

Scales a 3-vector as necessary to ensure that the result can be normalized
without loss of precision due to floating-point underflow.

This matches S2's EnsureNormalizable function.
"""
function ensureNormalizable(p::AbstractVector)
    if p == zeros(eltype(p), 3)
        error("Vector must be non-zero")
    end

    if !isNormalizable(p)

Scale so that the largest component has magnitude in [1, 2)

julia
        p_max = maximum(abs.(p))

Scale by 2^(-1-exponent) to achieve this range

julia
        return ldexp(2.0, -1 - exponent(Float64(p_max))) * p
    end

    return p
end

"""
    normalizableFromExact(xf::Vector{BigFloat})

Converts a BigFloat vector to a double-precision vector, scaling the
result as necessary to ensure that the result can be normalized without
loss of precision due to floating-point underflow.

This matches S2's NormalizableFromExact function.
"""
function normalizableFromExact(xf::AbstractVector{BigFloat})

First try a simple conversion

julia
    x = Float64.(xf)

    if isNormalizable(x)
        return x
    end

Find the largest exponent

julia
    max_exp = -1000000  # Very small initial value
    for i in 1:3
        if !iszero(xf[i])
            max_exp = max(max_exp, exponent(xf[i]))
        end
    end

    if max_exp < -1000000  # No non-zero components
        return zero(xf)
    end

Scale to get components in a good range

julia
    return Float64.(ldexp.(Float64.(xf), -max_exp))
end


"""
    isless_vector(a::AbstractVector, b::AbstractVector)

Lexicographic comparison of vectors.
This is used to establish a consistent order for symbolic perturbations.

Returns true if a comes before b in lexicographic order.
"""
function isless_vector(a::AbstractVector, b::AbstractVector)

First compare x coordinates

julia
    a[1] != b[1] && return a[1] < b[1]

If x coordinates are equal, compare y coordinates

julia
    a[2] != b[2] && return a[2] < b[2]

If both x and y are equal, compare z coordinates

julia
    return a[3] < b[3]
end

Use the Base.< function for UnitSphericalPoint to delegate to our isless_vector function TODO: this is sailing the high seas!

julia
function Base.:<(a::UnitSphericalPoint, b::UnitSphericalPoint)
    return isless_vector(a, b)
end

This page was generated using Literate.jl.