Das folgende Haskell-Programm berechnet Interpolationskurven, die folgenden Kriterien genügen:
-- Copyright (c) 2009 Jan Behrens -- -- Permission is hereby granted, free of charge, to any person obtaining a -- copy of this software and associated documentation files (the -- "Software"), to deal in the Software without restriction, including -- without limitation the rights to use, copy, modify, merge, publish, -- distribute, sublicense, and/or sell copies of the Software, and to -- permit persons to whom the Software is furnished to do so, subject to -- the following conditions: -- -- The above copyright notice and this permission notice shall be included -- in all copies or substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. enforceMonotony :: RealFloat a => [(a,a)] -> [(a,a)] enforceMonotony (p1@(x1,_):p2@(x2,_):rest) | x2 > x1 = p1 : enforceMonotony (p2:rest) | otherwise = error "X values are not strictly monotonic increasing" enforceMonotony points = points guessSlopes :: RealFloat a => [(a,a)] -> [(a,a,a)] guessSlopes points = beginWork (enforceMonotony points) where slope (x1,y1) (x2,y2) = (y2-y1) / (x2-x1) beginWork all@(p1@(x1,y1):p2:_) = (x1, y1, slope p1 p2) : work all beginWork _ = [] work [p1,p2@(x2,y2)] = [(x2, y2, slope p1 p2)] work (p1@(x1,y1) : p2@(x2,y2) : p3@(x3,y3) : rest) = (x2,y2,finalSlope) : work (p2:p3:rest) where leftSlope = slope p1 p2 rightSlope = slope p2 p3 mixAbsSlope = exp (((x3-x2) * log (abs leftSlope) + (x2-x1) * log (abs rightSlope)) / (x3-x1)) finalSlope | (y1 < y2) && (y2 < y3) = mixAbsSlope | (y1 > y2) && (y2 > y3) = negate mixAbsSlope | otherwise = 0 smoothPath :: RealFloat a => Int -> [(a,a)] -> [(a,a)] smoothPath _ [] = [] smoothPath steps points = head points' : work (guessSlopes points') where points' = enforceMonotony points work (d1:d2:rest) = pathPart d1 d2 ++ (work (d2:rest)) work _ = [] pathPart (x1,y1,s1) (x4,y4,s4) | y1 == y4 = [(x4,y4)] | otherwise = map interpolatePoint tx where rx = (x4-x1) / sqrt 2 / 2 ry = abs (y4-y1) / sqrt 2 / 2 dx2 = sqrt (1 / (1/rx^2 + s1^2/ry^2)) dy2 = s1 * dx2 x2 = x1 + dx2 y2 = y1 + dy2 dx3 = sqrt (1 / (1/rx^2 + s4^2/ry^2)) dy3 = s4 * dx3 x3 = x4 - dx3 y3 = y4 - dy3 tx = [ fromIntegral i / fromIntegral steps | i <- [1..steps]] bezierX = bezier x1 x2 x3 x4 bezierY = bezier y1 y2 y3 y4 interpolatePoint t = (bezierX t, bezierY t) bezier :: RealFloat a => a -> a -> a -> a -> a -> a bezier x1 x2 x3 x4 t = (1-t)^3 * x1 + 3 * t * (1-t)^2 * x2 + 3 * t^2 * (1-t) * x3 + t^3 * x4 gnuplotScript :: RealFloat a => [(a,a)] -> String gnuplotScript tuples = "plot '-' notitle with lines\n" ++ concat (map tupleFormat tuples) ++ "e\n" where tupleFormat (x,y) = show x ++ " " ++ show y ++ "\n" readPoints :: String -> [(Double, Double)] readPoints input = work (lines input) where work [] = [] work (line:lines) | parts == [] = work lines | parts == ["e"] = [] | length parts == 2 = (read part1, read part2) : work lines | otherwise = error "Parsing error" where parts = words line [part1,part2] = parts main = interact (gnuplotScript . smoothPath 60 . readPoints)
Die Datei smoothplot.hs ist herunterzuladen, und kann anschließend mit einem Haskell-Compiler compiliert werden:
$ ghc --make smoothplotEnthät die Datei "messwerte.txt" nun in jeder Zeile einen X-Wert gefolgt von Leerzeichen oder dem Tabulatorsteuerzeichen gefolgt von einem Y-Wert, dann lässt sich durch Eingabe des folgenden Kommandos eine Interpolationskurve zeichnen:
$ ./smoothplot < messwerte.txt | gnuplot -persist