{-# LANGUAGE BangPatterns #-} module MuAtom(MuAtom(..), MuProgress(..), muFromAddress, MuProgress'(..), muToAddress, MuProgress''(..), muLocate) where import Control.Arrow ((***)) import Data.Ratio ((%)) import Data.Vec (NearZero, nearZero) import Fractal.RUFF.Mandelbrot.Address (AngledInternalAddress, Angle, splitAddress, addressPeriod, externalAngles, angledInternalAddress) import Fractal.RUFF.Mandelbrot.Nucleus (findNucleus, findBond, findPeriod) import Fractal.RUFF.Mandelbrot.Ray (externalRay, externalRayOut) import Fractal.RUFF.Types.Complex (Complex, magnitude, magnitude2, phase, mkPolar) import Number (R) data MuAtom = MuAtom { muNucleus :: !(Complex R) , muSize :: !Double , muOrient :: !Double , muPeriod :: !Integer } deriving (Read, Show, Eq) data MuProgress = MuSplitTodo | MuSplitDone AngledInternalAddress [Angle] | MuAnglesTodo | MuAnglesDone !Rational !Rational | MuRayTodo | MuRay !Integer | MuRayDone !(Complex R) | MuNucleusTodo | MuNucleus !Integer | MuNucleusDone !(Complex R) | MuBondTodo | MuBond !Integer | MuBondDone !(Complex R) | MuSuccess !MuAtom | MuFailed deriving (Read, Show, Eq) muFromAddress :: AngledInternalAddress -> [MuProgress] muFromAddress addr = MuSplitTodo : let (!iaddr, !caddr) = splitAddress addr !p = addressPeriod iaddr in MuSplitDone iaddr caddr : MuAnglesTodo : case externalAngles iaddr of Nothing -> [MuFailed] Just (!lo, !hi) -> MuAnglesDone lo hi : MuRayTodo : let sharpness = 8 er = 65536 accuracy = 1e-10 ok w = magnitude2 w < 2 * er ^ (2::Int) -- NaN -> False rayl = externalRay accuracy sharpness er lo rayh = externalRay accuracy sharpness er hi ray' = takeWhile (uncurry (&&) . (ok *** ok) . snd) $ [ 1 .. ] `zip` (rayl `zip` rayh) rgo [] _ = [MuFailed] rgo [_] _ = [MuFailed] rgo ((i, (xl, xh)):m@((_, (yl, yh)):_)) f | i < fromIntegral sharpness * (p + 16) = MuRay i : rgo m f | dl + dh > dx + dy = MuRay i : rgo m f | otherwise = MuRayDone x : f x where x = 0.5 * (xl + xh) dl = magnitude2 (xl - yl) dh = magnitude2 (xh - yh) dx = magnitude2 (xl - xh) dy = magnitude2 (yl - yh) in rgo ray' $ \rayend -> MuNucleusTodo : let nuc = findNucleus p rayend nuc' = takeWhile (ok . snd) $ [ 1 .. ] `zip` nuc ngo [] _ = [MuFailed] ngo [_] _ = [MuFailed] ngo ((i, x):m@((_, y):_)) f | not (nearZero (x - y)) = MuNucleus i : ngo m f | otherwise = MuNucleusDone x : f x in ngo nuc' $ \nucleus -> MuBondTodo : let bnd = findBond p nucleus 0.5 bnd' = takeWhile (ok . snd) $ [ 1.. ] `zip` bnd bgo [] _ = [MuFailed] bgo [_] _ = [MuFailed] bgo ((i, x):m@((_, y):_)) f | not (nearZero (x - y)) = MuBond i : bgo m f | otherwise = MuBondDone x : f x in bgo bnd' $ \bond -> let delta = bond - nucleus size = realToFrac $ magnitude delta / 0.75 orient = realToFrac $ phase delta atom = MuAtom{ muNucleus = nucleus, muSize = size, muOrient = orient, muPeriod = p } in if 10 > size && size > 0 then [MuSuccess atom] else [MuFailed] data MuProgress' = MuCuspTodo | MuCuspDone !(Complex R) | MuDwellTodo | MuDwell !Integer | MuDwellDone !Integer | MuRayOutTodo | MuRayOut !Double | MuRayOutDone !(Complex R) | MuExternalTodo | MuExternalDone !Rational | MuAddressTodo | MuSuccess' AngledInternalAddress | MuFailed' deriving (Read, Show, Eq) muToAddress :: MuAtom -> [MuProgress'] muToAddress mu = MuCuspTodo : let cusp = muNucleus mu - mkPolar (realToFrac (muSize mu)) (realToFrac ((muOrient mu))) er = 65536 er2 = er * er in MuCuspDone cusp : MuDwellTodo : let dgo z n f = MuDwell n : if magnitude2 z > er2 then f n else dgo (z * z + cusp) (n + 1) f in dgo 0 0 $ \n -> MuDwellDone n : MuRayOutTodo : let rgo ((i,!_):izs@(_:_)) f = MuRayOut (fromIntegral i / (fromIntegral sharpness * fromIntegral n)) : rgo izs f rgo [(_,!z)] f | magnitude2 z > er2 = MuRayOutDone z : f z rgo _ _ = [MuFailed'] accuracy = 1e-16 sharpness = 16 epsilon0 = realToFrac (muSize mu) * accuracy in rgo ([(1 :: Integer) ..] `zip` externalRayOut (fromIntegral n + 100) epsilon0 accuracy sharpness er cusp) $ \rend -> MuExternalTodo : let den = 2 ^ muPeriod mu - 1 num' = fromIntegral den * warp (phase rend / (2 * pi)) num = round num' warp t | t > 0 = t | otherwise = t + 1 angle = num % den in MuExternalDone angle : MuAddressTodo : case angledInternalAddress angle of Nothing -> [MuFailed'] Just addr -> if addressPeriod addr /= muPeriod mu then [MuFailed'] else [MuSuccess' addr] data MuProgress'' = MuScanTodo | MuScan | MuScanDone !Integer | MuNucleusTodo' | MuNucleus' !Integer | MuNucleusDone' !(Complex R) | MuBondTodo' | MuBond' !Integer | MuBondDone' !(Complex R) | MuSuccess'' !MuAtom | MuFailed'' deriving (Read, Show, Eq) muLocate :: Complex R -> Double -> [MuProgress''] muLocate c r = MuScanTodo : MuScan : case findPeriod 10000000 (realToFrac r) c of Nothing -> [MuFailed''] Just p -> MuScanDone p : MuNucleusTodo' : let ok w = magnitude2 w < 16 -- NaN -> False nuc = findNucleus p c nuc' = takeWhile (ok . snd) $ [ 1 .. ] `zip` nuc ngo [] _ = [MuFailed''] ngo [_] _ = [MuFailed''] ngo ((i, x):m@((_, y):_)) f | not (nearZero (x - y)) = MuNucleus' i : ngo m f | otherwise = MuNucleusDone' x : f x in ngo nuc' $ \nucleus -> MuBondTodo' : let bnd = findBond p nucleus 0.5 bnd' = takeWhile (ok . snd) $ [ 1.. ] `zip` bnd bgo [] _ = [MuFailed''] bgo [_] _ = [MuFailed''] bgo ((i, x):m@((_, y):_)) f | not (nearZero (x - y)) = MuBond' i : bgo m f | otherwise = MuBondDone' x : f x in bgo bnd' $ \bond -> let delta = bond - nucleus size = realToFrac $ magnitude delta / 0.75 orient = realToFrac $ phase delta atom = MuAtom{ muNucleus = nucleus, muSize = size, muOrient = orient, muPeriod = p } in if 10 > size && size > 0 then [MuSuccess'' atom] else [MuFailed'']