zwirn-core-0.1.1.0: src/Zwirn/Core/Query.hs
{-# LANGUAGE BangPatterns #-}
module Zwirn.Core.Query where
{-
Query.hs - querying signals for breakpoints
(i.e. the zeroes of the fractional part of the inner time of a signal)
Copyright (C) 2025, Martin Gius
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library. If not, see <http://www.gnu.org/licenses/>.
-}
import Data.Bifunctor
import Zwirn.Core.Core
import Zwirn.Core.Time
import Zwirn.Core.Tree
import Zwirn.Core.Types
type Breakpoint st i a = (Time, Value i a, st)
precision :: Time
precision = 0.001
instance (Show a, Num st, ToList k) => Show (ZwirnT k st i a) where
show cord = show $ findAllValuesWithTime (Time 0 1, Time 1 1) 0 cord
findAllValuesWithTimeStateInfo :: (ToList k) => (Time, Time) -> st -> ZwirnT k st i a -> [(Time, a, st, [i])]
findAllValuesWithTimeStateInfo (from, to) st z = map fixTime $ findAllBreakpoints from to precision st z
where
fixTime (t, Value v d i, st) = if tDiff d > 0 then (t, v, st, i) else (t - precision, v, st, i)
findAllValuesWithTimeState :: (ToList k) => (Time, Time) -> st -> ZwirnT k st i a -> [(Time, a, st)]
findAllValuesWithTimeState (from, to) st z = map fixTime $ findAllBreakpoints from to precision st z
where
fixTime (t, Value v d _, st) = if tDiff d > 0 then (t, v, st) else (t - precision, v, st)
findAllValuesWithTime :: (ToList k) => (Time, Time) -> st -> ZwirnT k st i a -> [(Time, a)]
findAllValuesWithTime (from, to) st z = map fixTime $ findAllBreakpoints from to precision st z
where
fixTime (t, Value v d _, st) = if tDiff d > 0 then (t, v) else (t - precision, v)
findAll :: (ToList k) => (Time, Time) -> st -> ZwirnT k st i a -> [Breakpoint st i a]
findAll (from, to) st z = map fixTime $ findAllBreakpoints from to precision st z
where
fixTime (t, v@(Value _ d _), st) = if tDiff d > 0 then (t, v, st) else (t - precision, v, st)
findNextBreakpoint :: (ToList k) => Time -> Time -> Time -> st -> ZwirnT k st i a -> [Breakpoint st i a]
findNextBreakpoint time to precision st pat = findNextBreakpoint' time to precision start st pat
where
start = map (\(Value _ i _, _) -> frac i) $ toList $ unzwirn pat time st
findNextBreakpoint' :: (ToList k) => Time -> Time -> Time -> [Time] -> st -> ZwirnT k st i a -> [Breakpoint st i a]
findNextBreakpoint' !prevTime to precision prevs st pat
| now <= to = if or bps then concat $ zipWith zipper bps vs else findNextBreakpoint' now to precision times st pat
| or bps = concat $ zipWith zipper bps vs
| otherwise = []
where
now = prevTime + precision
vs = toList $ unzwirn pat now st
vals = map (value . fst) vs
times = map (frac . time . fst) vs
bps = breakConditions prevs times
zipper True (v, st') = [(now, v, st')]
zipper False _ = []
findAllBreakpoints :: (ToList k) => Time -> Time -> Time -> st -> ZwirnT k st i a -> [Breakpoint st i a]
findAllBreakpoints from to precision = findAllBreakpoints' (from - precision) (to - 2 * precision) precision
findAllBreakpoints' :: (ToList k) => Time -> Time -> Time -> st -> ZwirnT k st i a -> [Breakpoint st i a]
findAllBreakpoints' !from to precision st pat = case findNextBreakpoint from to precision st pat of
[] -> []
(bp@(t, _, st') : bps) -> bp : bps ++ findAllBreakpoints' t to precision st' pat
breakConditions :: [Time] -> [Time] -> [Bool]
breakConditions [] [] = []
breakConditions [] xs@(_ : _) = map breakCondition xs
breakConditions xs@(_ : _) [] = map breakCondition xs
breakConditions xs ys = liftA2Right breakConditionCombined xs ys
breakConditionCombined :: Time -> Time -> Bool
breakConditionCombined prev now = breakCondition now && not (breakCondition prev)
breakCondition :: Time -> Bool
breakCondition (Time inner scale)
| scale > 0 = inner <= tTime precision * abs scale
| abs (inner - 1) <= tTime precision * abs scale = True
| otherwise = False