-- | The binary64 coordinate kernel and exact predicate boundary. module Moonlight.Triangulation.Scalar ( scalarName , scalarByteSize , scalarBinaryFormat , scalarEpsilon , scalarUnitRoundoff , scalarCcwErrorBound , scalarInCircleErrorBound , orient2dCoordinates , inCircleCoordinates , BinaryFormat , formatRadix , formatMantissaDigits , formatExponentRange , minimumAllowedCoordinate , maximumAllowedCoordinate , canonicalScalarZero ) where import Moonlight.Triangulation.Internal.Dyadic (exactInCircleDet, exactOrientSignDouble) -- | The coordinate component of canonical point identity. IEEE signed zeros -- compare equal but hash differently by bits; every coordinate-keyed owner -- therefore normalizes them before storage or hashing. canonicalScalarZero :: Double -> Double canonicalScalarZero value | value == 0 = 0 | otherwise = value {-# INLINE canonicalScalarZero #-} data BinaryFormat = BinaryFormat { formatRadix :: !Integer , formatMantissaDigits :: !Int , formatExponentRange :: !(Int, Int) } deriving stock (Eq, Show) scalarName :: String scalarName = "binary64" scalarByteSize :: Int scalarByteSize = 8 scalarBinaryFormat :: BinaryFormat scalarBinaryFormat = BinaryFormat { formatRadix = floatRadix (0 :: Double) , formatMantissaDigits = floatDigits (0 :: Double) , formatExponentRange = floatRange (0 :: Double) } scalarEpsilon :: Double scalarEpsilon = 2.220446049250313e-16 scalarUnitRoundoff :: Double scalarUnitRoundoff = 1.1102230246251565e-16 scalarCcwErrorBound :: Double scalarCcwErrorBound = 3.3306690738754716e-16 scalarInCircleErrorBound :: Double scalarInCircleErrorBound = 1.1102230246251577e-15 orient2dCoordinates :: Double -> Double -> Double -> Double -> Double -> Double -> Ordering orient2dCoordinates = filteredOrient2dDouble inCircleCoordinates :: Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Ordering inCircleCoordinates = filteredInCircle scalarInCircleErrorBound -- The binary64 kernel pairs the approximation test with the -- fixed-precision exact sign, which answers the dyadic determinant's sign in -- machine words rather than allocated Integers whenever the exponent spread -- allows, and defers to the dyadic determinant when it does not. filteredOrient2dDouble :: Double -> Double -> Double -> Double -> Double -> Double -> Ordering filteredOrient2dDouble ax ay bx by cx cy | abs determinant > errorBound * determinantSum = compare determinant 0 | otherwise = exactOrientSignDouble ax ay bx by cx cy where errorBound = 3.3306690738754716e-16 !left = (ax - cx) * (by - cy) !right = (ay - cy) * (bx - cx) !determinant = left - right !determinantSum = abs left + abs right {-# INLINE filteredOrient2dDouble #-} filteredInCircle :: Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Ordering filteredInCircle errorBound ax ay bx by cx cy dx dy | abs determinant > errorBound * permanent = compare determinant 0 | otherwise = compare (exactInCircleDet ax ay bx by cx cy dx dy) 0 where !adx = ax - dx !ady = ay - dy !bdx = bx - dx !bdy = by - dy !cdx = cx - dx !cdy = cy - dy !abdet = adx * bdy - bdx * ady !bcdet = bdx * cdy - cdx * bdy !cadet = cdx * ady - adx * cdy !alift = adx * adx + ady * ady !blift = bdx * bdx + bdy * bdy !clift = cdx * cdx + cdy * cdy !determinant = alift * bcdet + blift * cadet + clift * abdet !permanent = (abs (bdx * cdy) + abs (cdx * bdy)) * alift + (abs (cdx * ady) + abs (adx * cdy)) * blift + (abs (adx * bdy) + abs (bdx * ady)) * clift {-# INLINE filteredInCircle #-} -- | The smallest coordinate magnitude the exact predicates accept. minimumAllowedCoordinate :: Double minimumAllowedCoordinate = 1.793662034335766e-43 -- | The largest coordinate magnitude the exact predicates accept. maximumAllowedCoordinate :: Double maximumAllowedCoordinate = 3.2138760885179806e60