From 93b7251f534a269589d4d317271ab86b7307c812 Mon Sep 17 00:00:00 2001 From: Yann Esposito Date: Thu, 14 Jun 2012 18:04:16 +0200 Subject: [PATCH] Update and regen --- .../html/en/blog/Haskell-OpenGL-Mandelbrot.md | 209 +++++++++++++----- .../html/fr/blog/Haskell-OpenGL-Mandelbrot.md | 209 +++++++++++++----- lib/y.rb | 5 + multi/blog/Haskell-OpenGL-Mandelbrot.md | 209 +++++++++++++----- output/Scratch/assets/css/main.css | 2 +- .../code/00_Introduction.lhs | 8 +- .../code/01_Introduction/hglmandel.lhs | 2 +- .../code/02_Edges/HGLMandelEdge.lhs | 26 ++- .../code/03_Mandelbulb/Mandelbulb.lhs | 24 +- .../code/04_Mandelbulb/Mandelbulb.lhs | 19 +- .../code/05_Mandelbulb/Mandelbulb.lhs | 71 +++--- .../code/05_Mandelbulb/YGL.hs | 15 +- .../code/06_Mandelbulb/Mandelbulb.lhs | 34 ++- .../code/07_Conclusion/Conclusion.lhs | 25 +++ .../code/00_Introduction.lhs | 8 +- .../code/01_Introduction/hglmandel.lhs | 2 +- .../code/02_Edges/HGLMandelEdge.lhs | 26 ++- .../code/03_Mandelbulb/Mandelbulb.lhs | 24 +- .../code/04_Mandelbulb/Mandelbulb.lhs | 19 +- .../code/05_Mandelbulb/Mandelbulb.lhs | 71 +++--- .../code/05_Mandelbulb/YGL.hs | 15 +- .../code/06_Mandelbulb/Mandelbulb.lhs | 34 ++- .../code/07_Conclusion/Conclusion.lhs | 25 +++ .../3DMandelbulbDetail.png | Bin 0 -> 103505 bytes .../3DMandelbulbDetail2.png | Bin 0 -> 59681 bytes output/Scratch/sitemap.xml | 6 +- 26 files changed, 771 insertions(+), 317 deletions(-) create mode 100644 output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs create mode 100644 output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs create mode 100644 output/Scratch/img/blog/Haskell-OpenGL-Mandelbrot/3DMandelbulbDetail.png create mode 100644 output/Scratch/img/blog/Haskell-OpenGL-Mandelbrot/3DMandelbulbDetail2.png diff --git a/content/html/en/blog/Haskell-OpenGL-Mandelbrot.md b/content/html/en/blog/Haskell-OpenGL-Mandelbrot.md index 0b9dd9764..dc784c496 100644 --- a/content/html/en/blog/Haskell-OpenGL-Mandelbrot.md +++ b/content/html/en/blog/Haskell-OpenGL-Mandelbrot.md @@ -69,14 +69,16 @@ and something nice to see in 3D. Here is what you'll end with: -blogimage("GoldenMandelbulb.png","A golden mandelbulb") +blogfigure("GoldenMandelbulb.png","The entire Mandelbulb") +blogfigure("3DMandelbulbDetail.png","A Mandelbulb detail") +blogfigure("3DMandelbulbDetail2.png","Another detail of the Mandelbulb") And here are the intermediate steps: blogimage("HGL_Plan.png","The parts of the article") -From 1 to 3 it will be _dirtier_ and _dirtier_. -We start cleaning everything at the 4th part. +From the 2nd section to the 4th it will be _dirtier_ and _dirtier_. +We start cleaning everything at the 5th section.
Download the source code of this section → 01_Introduction/hglmandel.lhs @@ -264,7 +266,7 @@ mandel x y = It uses the main Mandelbrot function for each complex \\(c\\). -The Mandelbrot set is the set of complex number c such that the following sequence does not escape to infinity. +The Mandelbrot set is the set of complex number \\(c\\) such that the following sequence does not escape to infinity. Let us define \\(f_c: \mathbb{C} \to \mathbb{C}\\) @@ -371,7 +373,7 @@ The method I use is a rough approximation. I consider the Mandelbrot set to be almost convex. The result will be good enough. -We change slightly the drawMandelbrot function. +We change slightly the `drawMandelbrot` function. We replace the `Points` by `LineLoop`
@@ -401,14 +403,14 @@ allPoints = positivePoints ++
We only need to compute the positive point. -The Mandelbrot set is symmetric on the abscisse axis. +The Mandelbrot set is symmetric relatively to the abscisse axis.
positivePoints :: [(GLfloat,GLfloat,Color3 GLfloat)] positivePoints = do x <- [-width..width] - let y = findMaxOrdFor (mandel x) 0 height (log2 height) + let y = maxZeroIndex (mandel x) 0 height (log2 height) if y < 1 -- We don't draw point in the absciss then [] else return (x/width,y/height,colorFromValue $ mandel x y) @@ -420,24 +422,32 @@ positivePoints = do This function is interesting. For those not used to the list monad here is a natural language version of this function: -~~~ + positivePoints = for all x in the range [-width..width] let y be smallest number s.t. mandel x y > 0 if y is on 0 then don't return a point else return the value corresonding to (x,y,color for (x+iy)) -~~~ + In fact using the list monad you write like if you consider only one element at a time and the computation is done non deterministically. To find the smallest number such that `mandel x y > 0` we use a simple dichotomy:
-findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2
@@ -497,7 +507,7 @@ But it will be enough for us to create something that look nice. This section is quite long, but don't be afraid, most of the code is some OpenGL boilerplate. -For those you want to skim, +If you just want to skim this section, here is a high level representation: > - OpenGL Boilerplate @@ -787,7 +797,7 @@ depthPoints = do x <- [-width..width] y <- [-height..height] let - depthOf x' y' = findMaxOrdFor (mandel x' y') 0 deep logdeep + depthOf x' y' = maxZeroIndex (mandel x' y') 0 deep logdeep logdeep = floor ((log deep) / log 2) z1 = depthOf x y z2 = depthOf (x+1) y @@ -818,7 +828,7 @@ depthPoints = do y <- [-height..height] let neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] - depthOf (u,v) = findMaxOrdFor (mandel u v) 0 deep logdeep + depthOf (u,v) = maxZeroIndex (mandel u v) 0 deep logdeep logdeep = floor ((log deep) / log 2) -- zs are 3D points with found depth zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors @@ -858,11 +868,21 @@ The rest of the program is very close to the preceding one.
-findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => + (a -> b) -> a -> a -> Int -> a +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2
@@ -978,7 +998,7 @@ depthPoints = do y <- [0..height] let neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] - depthOf (u,v) = findMaxOrdFor (ymandel u v) 0 deep 7 + depthOf (u,v) = maxZeroIndex (ymandel u v) 0 deep 7 -- zs are 3D points with found depth zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors -- ts are 3D pixels + mandel value @@ -992,11 +1012,20 @@ depthPoints = do -- Draw two triangles else [ps!!0,ps!!1,ps!!2,ps!!0,ps!!2,ps!!3] -findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = + +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 colorFromValue n = @@ -1026,21 +1055,26 @@ For example, we provide ordered vertices. Some points: -1. OpenGL and GLUT are linked to the C library. +1. OpenGL and GLUT is done in C. In particular the `mainLoop` function is a direct link to the C library (FFI). - This function if so far from the pure spirit of functional languages. + This function is clearly far from the functional paradigm. Could we make this better? - We will have two choices, or create our own `mainLoop` function to make it more functional. - Or deal with the imperative nature of the GLUT `mainLoop` function. - As a goal of this article is to understand how to deal with existing library and particularly the one coming from imperative language we will continue to use the `mainLoop` function. -2. Or main problem come from user interaction. - If you ask the Internet, about how to deal with user interaction with a functional paradigm, the main answer is to use _functional reactive programming_ (FRP). - I read very few about FRP, and I might be completely wrong when I say that it is about creating a DSL where atoms are time functions. - While I'm writing these lines, I don't know if I'll do something looking close to that. - For now I'll simply try to resolve the first problem. + We will have two choices: -Then here is how I imagine things should go. -First, what the main loop should look like: + - create our own `mainLoop` function to make it more functional. + - deal with the imperative nature of the GLUT `mainLoop` function. + + As one of the goal of this article is to understand how to deal with existing libraries and particularly the one coming from imperative languages, we will continue to use the `mainLoop` function. +2. Our main problem come from user interaction. + If you ask "the Internet", + about how to deal with user interaction with a functional paradigm, + the main answer is to use _functional reactive programming_ (FRP). + I won't use FRP in this article. + Instead, I'll use a simpler while less effective way to deal with user interaction. + But The method I'll use will be as pure and functional as possible. + +Here is how I imagine things should go. +First, what the main loop should look like if we could make our own: functionalMainLoop = @@ -1127,6 +1161,14 @@ instance DisplayableWorld World where camPos = position w, camDir = angle w, camZoom = scale w } + -- objects for world w + -- is the list of one unique element + -- The element is an YObject + -- more precisely the XYFunc Function3D Box3D + -- where the Function3D is the type + -- Point -> Point -> Maybe (Point,Color) + -- and its value here is ((shape w) res) + -- and the Box3D value is defbox objects w = [XYFunc ((shape w) res) defbox] where res = resolution $ box w @@ -1154,8 +1196,8 @@ zdir = makePoint3D (0,0,1)
-Note `(-*<)` is scalar product. -Also note we could add Point3D as numbers. +Note `(-*<)` is the scalar product (`α -*< (x,y,z) = (αx,αy,αz)`). +Also note we could add two Point3D.
@@ -1250,9 +1292,9 @@ Because we consider partial functions shapeFunc :: Scalar -> Function3D shapeFunc res x y = let - z = findMaxOrdFor (ymandel x y) 0 1 20 + z = maxZeroIndex (ymandel x y) 0 1 20 in - if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | + if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | val <- [res], xeps <- [-val,val], yeps<-[-val,val]] then Nothing else Just (z,colorFromValue ((ymandel x y z) * 64)) @@ -1277,13 +1319,21 @@ The rest is similar to the preceding sections.
-findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => (a -> b) -> a -> a -> Int -> a -findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = - if func medpoint /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = + if (func medpoint) /= 0 + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 ymandel :: Point -> Point -> Point -> Point @@ -1291,9 +1341,9 @@ ymandel x y z = fromIntegral (mandel x y z 64) / 64
-I won't put how the magic occurs directly here. -But all the magic occurs in the file `YGL.hs`. -This file is commented a lot. +I won't explain how the magic occurs here. +If you are interested, just read the file [`YGL.hs`](code/05_Mandelbulb/YGL.hs). +It is commented a lot. - [`YGL.hs`](code/05_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/05_Mandelbulb/Mandel.hs), the mandel function @@ -1306,7 +1356,8 @@ This file is commented a lot. ## Optimization From the architecture stand point all is clear. -If you read the code, you'll see I didn't made everything perfect, for example, I didn't coded nicely the lights. +If you read the code of `YGL.hs`, you'll see I didn't made everything perfect. +For example, I didn't finished the code of the lights. But I believe it is a good first step and it will be easy to go further. The separation between rendering and world behavior is clear. Unfortunately the program of the preceding session is extremely slow. @@ -1449,13 +1500,14 @@ initialWorld = World { , anglePerSec = 5.0 , position = makePoint3D (0,0,0) , scale = 1.0 - , box = Box3D { minPoint = makePoint3D (-2,-2,-2) - , maxPoint = makePoint3D (2,2,2) - , resolution = 0.03 } + , box = Box3D { minPoint = makePoint3D (0-eps, 0-eps, 0-eps) + , maxPoint = makePoint3D (0+eps, 0+eps, 0+eps) + , resolution = 0.02 } , told = 0 -- We declare cache directly this time , cache = objectFunctionFromWorld initialWorld } + where eps=2
@@ -1520,9 +1572,9 @@ shapeFunc' res x y = if or [tmp u v>=0 | u<-[x,x+res], v<-[y,y+res]] shapeFunc :: Scalar -> Function3D shapeFunc res x y = let - z = findMaxOrdFor (ymandel x y) 0 1 20 + z = maxZeroIndex (ymandel x y) 0 1 20 in - if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | + if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | val <- [res], xeps <- [-val,val], yeps<-[-val,val]] then Nothing else Just (z,colorFromValue 0) @@ -1535,13 +1587,21 @@ colorFromValue n = in makeColor (t n) (t (n+5)) (t (n+10)) -findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => (a -> b) -> a -> a -> Int -> a -findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +maxZeroIndex _ minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if func medpoint /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 ymandel :: Point -> Point -> Point -> Point @@ -1551,9 +1611,36 @@ ymandel x y z = fromIntegral (mandel x y z 64) / 64 +And you can also consider small changes in other source files. + - [`YGL.hs`](code/06_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/06_Mandelbulb/Mandel.hs), the mandel function - [`ExtComplex`](code/06_Mandelbulb/ExtComplex.hs), the extended complexes Download the source code of this section → 06_Mandelbulb/Mandelbulb.lhs +## Conclusion + +As we can use imperative style in a functional language, +know you can use functional style in imperative languages. +This article exposed a way to organize some code in a functional way. +I'd like to stress the usage of Haskell made it very simple to achieve this. + +Once you are used to pure functional style, +it is hard not to see all advantages it offers. + +The code in the two last sections is completely pure and functional. +Furthermore I don't use `GLfloat`, `Color3` or any other OpenGL type. +If I want to use another library in the future, +I would be able to keep all the pure code and simply update the YGL module. + +The `YGL` module can be seen as a "wrapper" around 3D display and user interaction. +It is a clean separator between the imperative paradigm and functional paradigm. + +If you want to go further, it shouldn't be hard to add parallelism. +This should be easy mainly because most of the visible code is pure. +Such an optimization would have been harder by using directly the OpenGL library. + +You should also want to make a more precise object. Because, the Mandelbulb is +clearly not convex. But a precise rendering might be very long from +O(n².log(n)) to O(n³). diff --git a/content/html/fr/blog/Haskell-OpenGL-Mandelbrot.md b/content/html/fr/blog/Haskell-OpenGL-Mandelbrot.md index 9a2ae42a0..66fa3dd88 100644 --- a/content/html/fr/blog/Haskell-OpenGL-Mandelbrot.md +++ b/content/html/fr/blog/Haskell-OpenGL-Mandelbrot.md @@ -69,14 +69,16 @@ and something nice to see in 3D. Here is what you'll end with: -blogimage("GoldenMandelbulb.png","A golden mandelbulb") +blogfigure("GoldenMandelbulb.png","The entire Mandelbulb") +blogfigure("3DMandelbulbDetail.png","A Mandelbulb detail") +blogfigure("3DMandelbulbDetail2.png","Another detail of the Mandelbulb") And here are the intermediate steps: blogimage("HGL_Plan.png","The parts of the article") -From 1 to 3 it will be _dirtier_ and _dirtier_. -We start cleaning everything at the 4th part. +From the 2nd section to the 4th it will be _dirtier_ and _dirtier_. +We start cleaning everything at the 5th section.
Download the source code of this section → 01_Introduction/hglmandel.lhs @@ -264,7 +266,7 @@ mandel x y = It uses the main Mandelbrot function for each complex \\(c\\). -The Mandelbrot set is the set of complex number c such that the following sequence does not escape to infinity. +The Mandelbrot set is the set of complex number \\(c\\) such that the following sequence does not escape to infinity. Let us define \\(f_c: \mathbb{C} \to \mathbb{C}\\) @@ -371,7 +373,7 @@ The method I use is a rough approximation. I consider the Mandelbrot set to be almost convex. The result will be good enough. -We change slightly the drawMandelbrot function. +We change slightly the `drawMandelbrot` function. We replace the `Points` by `LineLoop`
@@ -401,14 +403,14 @@ allPoints = positivePoints ++
We only need to compute the positive point. -The Mandelbrot set is symmetric on the abscisse axis. +The Mandelbrot set is symmetric relatively to the abscisse axis.
positivePoints :: [(GLfloat,GLfloat,Color3 GLfloat)] positivePoints = do x <- [-width..width] - let y = findMaxOrdFor (mandel x) 0 height (log2 height) + let y = maxZeroIndex (mandel x) 0 height (log2 height) if y < 1 -- We don't draw point in the absciss then [] else return (x/width,y/height,colorFromValue $ mandel x y) @@ -420,24 +422,32 @@ positivePoints = do This function is interesting. For those not used to the list monad here is a natural language version of this function: -~~~ + positivePoints = for all x in the range [-width..width] let y be smallest number s.t. mandel x y > 0 if y is on 0 then don't return a point else return the value corresonding to (x,y,color for (x+iy)) -~~~ + In fact using the list monad you write like if you consider only one element at a time and the computation is done non deterministically. To find the smallest number such that `mandel x y > 0` we use a simple dichotomy:
-findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2
@@ -497,7 +507,7 @@ But it will be enough for us to create something that look nice. This section is quite long, but don't be afraid, most of the code is some OpenGL boilerplate. -For those you want to skim, +If you just want to skim this section, here is a high level representation: > - OpenGL Boilerplate @@ -787,7 +797,7 @@ depthPoints = do x <- [-width..width] y <- [-height..height] let - depthOf x' y' = findMaxOrdFor (mandel x' y') 0 deep logdeep + depthOf x' y' = maxZeroIndex (mandel x' y') 0 deep logdeep logdeep = floor ((log deep) / log 2) z1 = depthOf x y z2 = depthOf (x+1) y @@ -818,7 +828,7 @@ depthPoints = do y <- [-height..height] let neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] - depthOf (u,v) = findMaxOrdFor (mandel u v) 0 deep logdeep + depthOf (u,v) = maxZeroIndex (mandel u v) 0 deep logdeep logdeep = floor ((log deep) / log 2) -- zs are 3D points with found depth zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors @@ -858,11 +868,21 @@ The rest of the program is very close to the preceding one.
-findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => + (a -> b) -> a -> a -> Int -> a +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2
@@ -978,7 +998,7 @@ depthPoints = do y <- [0..height] let neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] - depthOf (u,v) = findMaxOrdFor (ymandel u v) 0 deep 7 + depthOf (u,v) = maxZeroIndex (ymandel u v) 0 deep 7 -- zs are 3D points with found depth zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors -- ts are 3D pixels + mandel value @@ -992,11 +1012,20 @@ depthPoints = do -- Draw two triangles else [ps!!0,ps!!1,ps!!2,ps!!0,ps!!2,ps!!3] -findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = + +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 colorFromValue n = @@ -1026,21 +1055,26 @@ For example, we provide ordered vertices. Some points: -1. OpenGL and GLUT are linked to the C library. +1. OpenGL and GLUT is done in C. In particular the `mainLoop` function is a direct link to the C library (FFI). - This function if so far from the pure spirit of functional languages. + This function is clearly far from the functional paradigm. Could we make this better? - We will have two choices, or create our own `mainLoop` function to make it more functional. - Or deal with the imperative nature of the GLUT `mainLoop` function. - As a goal of this article is to understand how to deal with existing library and particularly the one coming from imperative language we will continue to use the `mainLoop` function. -2. Or main problem come from user interaction. - If you ask the Internet, about how to deal with user interaction with a functional paradigm, the main answer is to use _functional reactive programming_ (FRP). - I read very few about FRP, and I might be completely wrong when I say that it is about creating a DSL where atoms are time functions. - While I'm writing these lines, I don't know if I'll do something looking close to that. - For now I'll simply try to resolve the first problem. + We will have two choices: -Then here is how I imagine things should go. -First, what the main loop should look like: + - create our own `mainLoop` function to make it more functional. + - deal with the imperative nature of the GLUT `mainLoop` function. + + As one of the goal of this article is to understand how to deal with existing libraries and particularly the one coming from imperative languages, we will continue to use the `mainLoop` function. +2. Our main problem come from user interaction. + If you ask "the Internet", + about how to deal with user interaction with a functional paradigm, + the main answer is to use _functional reactive programming_ (FRP). + I won't use FRP in this article. + Instead, I'll use a simpler while less effective way to deal with user interaction. + But The method I'll use will be as pure and functional as possible. + +Here is how I imagine things should go. +First, what the main loop should look like if we could make our own: functionalMainLoop = @@ -1127,6 +1161,14 @@ instance DisplayableWorld World where camPos = position w, camDir = angle w, camZoom = scale w } + -- objects for world w + -- is the list of one unique element + -- The element is an YObject + -- more precisely the XYFunc Function3D Box3D + -- where the Function3D is the type + -- Point -> Point -> Maybe (Point,Color) + -- and its value here is ((shape w) res) + -- and the Box3D value is defbox objects w = [XYFunc ((shape w) res) defbox] where res = resolution $ box w @@ -1154,8 +1196,8 @@ zdir = makePoint3D (0,0,1)
-Note `(-*<)` is scalar product. -Also note we could add Point3D as numbers. +Note `(-*<)` is the scalar product (`α -*< (x,y,z) = (αx,αy,αz)`). +Also note we could add two Point3D.
@@ -1250,9 +1292,9 @@ Because we consider partial functions shapeFunc :: Scalar -> Function3D shapeFunc res x y = let - z = findMaxOrdFor (ymandel x y) 0 1 20 + z = maxZeroIndex (ymandel x y) 0 1 20 in - if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | + if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | val <- [res], xeps <- [-val,val], yeps<-[-val,val]] then Nothing else Just (z,colorFromValue ((ymandel x y z) * 64)) @@ -1277,13 +1319,21 @@ The rest is similar to the preceding sections.
-findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => (a -> b) -> a -> a -> Int -> a -findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = - if func medpoint /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = + if (func medpoint) /= 0 + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 ymandel :: Point -> Point -> Point -> Point @@ -1291,9 +1341,9 @@ ymandel x y z = fromIntegral (mandel x y z 64) / 64
-I won't put how the magic occurs directly here. -But all the magic occurs in the file `YGL.hs`. -This file is commented a lot. +I won't explain how the magic occurs here. +If you are interested, just read the file [`YGL.hs`](code/05_Mandelbulb/YGL.hs). +It is commented a lot. - [`YGL.hs`](code/05_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/05_Mandelbulb/Mandel.hs), the mandel function @@ -1306,7 +1356,8 @@ This file is commented a lot. ## Optimization From the architecture stand point all is clear. -If you read the code, you'll see I didn't made everything perfect, for example, I didn't coded nicely the lights. +If you read the code of `YGL.hs`, you'll see I didn't made everything perfect. +For example, I didn't finished the code of the lights. But I believe it is a good first step and it will be easy to go further. The separation between rendering and world behavior is clear. Unfortunately the program of the preceding session is extremely slow. @@ -1449,13 +1500,14 @@ initialWorld = World { , anglePerSec = 5.0 , position = makePoint3D (0,0,0) , scale = 1.0 - , box = Box3D { minPoint = makePoint3D (-2,-2,-2) - , maxPoint = makePoint3D (2,2,2) - , resolution = 0.03 } + , box = Box3D { minPoint = makePoint3D (0-eps, 0-eps, 0-eps) + , maxPoint = makePoint3D (0+eps, 0+eps, 0+eps) + , resolution = 0.02 } , told = 0 -- We declare cache directly this time , cache = objectFunctionFromWorld initialWorld } + where eps=2
@@ -1520,9 +1572,9 @@ shapeFunc' res x y = if or [tmp u v>=0 | u<-[x,x+res], v<-[y,y+res]] shapeFunc :: Scalar -> Function3D shapeFunc res x y = let - z = findMaxOrdFor (ymandel x y) 0 1 20 + z = maxZeroIndex (ymandel x y) 0 1 20 in - if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | + if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | val <- [res], xeps <- [-val,val], yeps<-[-val,val]] then Nothing else Just (z,colorFromValue 0) @@ -1535,13 +1587,21 @@ colorFromValue n = in makeColor (t n) (t (n+5)) (t (n+10)) -findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => (a -> b) -> a -> a -> Int -> a -findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +maxZeroIndex _ minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if func medpoint /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 ymandel :: Point -> Point -> Point -> Point @@ -1551,9 +1611,36 @@ ymandel x y z = fromIntegral (mandel x y z 64) / 64 +And you can also consider small changes in other source files. + - [`YGL.hs`](code/06_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/06_Mandelbulb/Mandel.hs), the mandel function - [`ExtComplex`](code/06_Mandelbulb/ExtComplex.hs), the extended complexes Download the source code of this section → 06_Mandelbulb/Mandelbulb.lhs +## Conclusion + +As we can use imperative style in a functional language, +know you can use functional style in imperative languages. +This article exposed a way to organize some code in a functional way. +I'd like to stress the usage of Haskell made it very simple to achieve this. + +Once you are used to pure functional style, +it is hard not to see all advantages it offers. + +The code in the two last sections is completely pure and functional. +Furthermore I don't use `GLfloat`, `Color3` or any other OpenGL type. +If I want to use another library in the future, +I would be able to keep all the pure code and simply update the YGL module. + +The `YGL` module can be seen as a "wrapper" around 3D display and user interaction. +It is a clean separator between the imperative paradigm and functional paradigm. + +If you want to go further, it shouldn't be hard to add parallelism. +This should be easy mainly because most of the visible code is pure. +Such an optimization would have been harder by using directly the OpenGL library. + +You should also want to make a more precise object. Because, the Mandelbulb is +clearly not convex. But a precise rendering might be very long from +O(n².log(n)) to O(n³). diff --git a/lib/y.rb b/lib/y.rb index 96d32a5d7..b81a9b884 100644 --- a/lib/y.rb +++ b/lib/y.rb @@ -184,6 +184,11 @@ module Nanoc3::Filters # DEBUG # puts "cssclass: #{cssclass}" # DEBUG # puts "=====================" blogimage(filename,title,%{#{cssclass} #{position}}) + end.gsub(/blogfigure\s*\(\s*"([^"]*)"\s*,\s*"([^"]*)"\s*(,\s*"([^"]*)"\s*)?\)/) do + filename=$1 + title=$2 + cssclass=$4 + blogfigure(filename,title,cssclass) end end end diff --git a/multi/blog/Haskell-OpenGL-Mandelbrot.md b/multi/blog/Haskell-OpenGL-Mandelbrot.md index 4762fd9b4..f7bec6f7b 100644 --- a/multi/blog/Haskell-OpenGL-Mandelbrot.md +++ b/multi/blog/Haskell-OpenGL-Mandelbrot.md @@ -72,14 +72,16 @@ and something nice to see in 3D. Here is what you'll end with: -blogimage("GoldenMandelbulb.png","A golden mandelbulb") +blogfigure("GoldenMandelbulb.png","The entire Mandelbulb") +blogfigure("3DMandelbulbDetail.png","A Mandelbulb detail") +blogfigure("3DMandelbulbDetail2.png","Another detail of the Mandelbulb") And here are the intermediate steps: blogimage("HGL_Plan.png","The parts of the article") -From 1 to 3 it will be _dirtier_ and _dirtier_. -We start cleaning everything at the 4th part. +From the 2nd section to the 4th it will be _dirtier_ and _dirtier_. +We start cleaning everything at the 5th section.
Download the source code of this section → 01_Introduction/hglmandel.lhs @@ -267,7 +269,7 @@ mandel x y = It uses the main Mandelbrot function for each complex \\(c\\). -The Mandelbrot set is the set of complex number c such that the following sequence does not escape to infinity. +The Mandelbrot set is the set of complex number \\(c\\) such that the following sequence does not escape to infinity. Let us define \\(f_c: \mathbb{C} \to \mathbb{C}\\) @@ -374,7 +376,7 @@ The method I use is a rough approximation. I consider the Mandelbrot set to be almost convex. The result will be good enough. -We change slightly the drawMandelbrot function. +We change slightly the `drawMandelbrot` function. We replace the `Points` by `LineLoop`
@@ -404,14 +406,14 @@ allPoints = positivePoints ++
We only need to compute the positive point. -The Mandelbrot set is symmetric on the abscisse axis. +The Mandelbrot set is symmetric relatively to the abscisse axis.
positivePoints :: [(GLfloat,GLfloat,Color3 GLfloat)] positivePoints = do x <- [-width..width] - let y = findMaxOrdFor (mandel x) 0 height (log2 height) + let y = maxZeroIndex (mandel x) 0 height (log2 height) if y < 1 -- We don't draw point in the absciss then [] else return (x/width,y/height,colorFromValue $ mandel x y) @@ -423,24 +425,32 @@ positivePoints = do This function is interesting. For those not used to the list monad here is a natural language version of this function: -~~~ + positivePoints = for all x in the range [-width..width] let y be smallest number s.t. mandel x y > 0 if y is on 0 then don't return a point else return the value corresonding to (x,y,color for (x+iy)) -~~~ + In fact using the list monad you write like if you consider only one element at a time and the computation is done non deterministically. To find the smallest number such that `mandel x y > 0` we use a simple dichotomy:
-findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2
@@ -500,7 +510,7 @@ But it will be enough for us to create something that look nice. This section is quite long, but don't be afraid, most of the code is some OpenGL boilerplate. -For those you want to skim, +If you just want to skim this section, here is a high level representation: > - OpenGL Boilerplate @@ -790,7 +800,7 @@ depthPoints = do x <- [-width..width] y <- [-height..height] let - depthOf x' y' = findMaxOrdFor (mandel x' y') 0 deep logdeep + depthOf x' y' = maxZeroIndex (mandel x' y') 0 deep logdeep logdeep = floor ((log deep) / log 2) z1 = depthOf x y z2 = depthOf (x+1) y @@ -821,7 +831,7 @@ depthPoints = do y <- [-height..height] let neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] - depthOf (u,v) = findMaxOrdFor (mandel u v) 0 deep logdeep + depthOf (u,v) = maxZeroIndex (mandel u v) 0 deep logdeep logdeep = floor ((log deep) / log 2) -- zs are 3D points with found depth zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors @@ -861,11 +871,21 @@ The rest of the program is very close to the preceding one.
-findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => + (a -> b) -> a -> a -> Int -> a +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2
@@ -981,7 +1001,7 @@ depthPoints = do y <- [0..height] let neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] - depthOf (u,v) = findMaxOrdFor (ymandel u v) 0 deep 7 + depthOf (u,v) = maxZeroIndex (ymandel u v) 0 deep 7 -- zs are 3D points with found depth zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors -- ts are 3D pixels + mandel value @@ -995,11 +1015,20 @@ depthPoints = do -- Draw two triangles else [ps!!0,ps!!1,ps!!2,ps!!0,ps!!2,ps!!3] -findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = + +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if (func medpoint) /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 colorFromValue n = @@ -1029,21 +1058,26 @@ For example, we provide ordered vertices. Some points: -1. OpenGL and GLUT are linked to the C library. +1. OpenGL and GLUT is done in C. In particular the `mainLoop` function is a direct link to the C library (FFI). - This function if so far from the pure spirit of functional languages. + This function is clearly far from the functional paradigm. Could we make this better? - We will have two choices, or create our own `mainLoop` function to make it more functional. - Or deal with the imperative nature of the GLUT `mainLoop` function. - As a goal of this article is to understand how to deal with existing library and particularly the one coming from imperative language we will continue to use the `mainLoop` function. -2. Or main problem come from user interaction. - If you ask the Internet, about how to deal with user interaction with a functional paradigm, the main answer is to use _functional reactive programming_ (FRP). - I read very few about FRP, and I might be completely wrong when I say that it is about creating a DSL where atoms are time functions. - While I'm writing these lines, I don't know if I'll do something looking close to that. - For now I'll simply try to resolve the first problem. + We will have two choices: -Then here is how I imagine things should go. -First, what the main loop should look like: + - create our own `mainLoop` function to make it more functional. + - deal with the imperative nature of the GLUT `mainLoop` function. + + As one of the goal of this article is to understand how to deal with existing libraries and particularly the one coming from imperative languages, we will continue to use the `mainLoop` function. +2. Our main problem come from user interaction. + If you ask "the Internet", + about how to deal with user interaction with a functional paradigm, + the main answer is to use _functional reactive programming_ (FRP). + I won't use FRP in this article. + Instead, I'll use a simpler while less effective way to deal with user interaction. + But The method I'll use will be as pure and functional as possible. + +Here is how I imagine things should go. +First, what the main loop should look like if we could make our own: functionalMainLoop = @@ -1130,6 +1164,14 @@ instance DisplayableWorld World where camPos = position w, camDir = angle w, camZoom = scale w } + -- objects for world w + -- is the list of one unique element + -- The element is an YObject + -- more precisely the XYFunc Function3D Box3D + -- where the Function3D is the type + -- Point -> Point -> Maybe (Point,Color) + -- and its value here is ((shape w) res) + -- and the Box3D value is defbox objects w = [XYFunc ((shape w) res) defbox] where res = resolution $ box w @@ -1157,8 +1199,8 @@ zdir = makePoint3D (0,0,1)
-Note `(-*<)` is scalar product. -Also note we could add Point3D as numbers. +Note `(-*<)` is the scalar product (`α -*< (x,y,z) = (αx,αy,αz)`). +Also note we could add two Point3D.
@@ -1253,9 +1295,9 @@ Because we consider partial functions shapeFunc :: Scalar -> Function3D shapeFunc res x y = let - z = findMaxOrdFor (ymandel x y) 0 1 20 + z = maxZeroIndex (ymandel x y) 0 1 20 in - if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | + if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | val <- [res], xeps <- [-val,val], yeps<-[-val,val]] then Nothing else Just (z,colorFromValue ((ymandel x y z) * 64)) @@ -1280,13 +1322,21 @@ The rest is similar to the preceding sections.
-findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => (a -> b) -> a -> a -> Int -> a -findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = - if func medpoint /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) +maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = + if (func medpoint) /= 0 + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 ymandel :: Point -> Point -> Point -> Point @@ -1294,9 +1344,9 @@ ymandel x y z = fromIntegral (mandel x y z 64) / 64
-I won't put how the magic occurs directly here. -But all the magic occurs in the file `YGL.hs`. -This file is commented a lot. +I won't explain how the magic occurs here. +If you are interested, just read the file [`YGL.hs`](code/05_Mandelbulb/YGL.hs). +It is commented a lot. - [`YGL.hs`](code/05_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/05_Mandelbulb/Mandel.hs), the mandel function @@ -1309,7 +1359,8 @@ This file is commented a lot. ## Optimization From the architecture stand point all is clear. -If you read the code, you'll see I didn't made everything perfect, for example, I didn't coded nicely the lights. +If you read the code of `YGL.hs`, you'll see I didn't made everything perfect. +For example, I didn't finished the code of the lights. But I believe it is a good first step and it will be easy to go further. The separation between rendering and world behavior is clear. Unfortunately the program of the preceding session is extremely slow. @@ -1452,13 +1503,14 @@ initialWorld = World { , anglePerSec = 5.0 , position = makePoint3D (0,0,0) , scale = 1.0 - , box = Box3D { minPoint = makePoint3D (-2,-2,-2) - , maxPoint = makePoint3D (2,2,2) - , resolution = 0.03 } + , box = Box3D { minPoint = makePoint3D (0-eps, 0-eps, 0-eps) + , maxPoint = makePoint3D (0+eps, 0+eps, 0+eps) + , resolution = 0.02 } , told = 0 -- We declare cache directly this time , cache = objectFunctionFromWorld initialWorld } + where eps=2
@@ -1523,9 +1575,9 @@ shapeFunc' res x y = if or [tmp u v>=0 | u<-[x,x+res], v<-[y,y+res]] shapeFunc :: Scalar -> Function3D shapeFunc res x y = let - z = findMaxOrdFor (ymandel x y) 0 1 20 + z = maxZeroIndex (ymandel x y) 0 1 20 in - if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | + if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | val <- [res], xeps <- [-val,val], yeps<-[-val,val]] then Nothing else Just (z,colorFromValue 0) @@ -1538,13 +1590,21 @@ colorFromValue n = in makeColor (t n) (t (n+5)) (t (n+10)) -findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +-- given f min max nbtest, +-- considering +-- - f is an increasing function +-- - f(min)=0 +-- - f(max)≠0 +-- then maxZeroIndex f min max nbtest returns x such that +-- f(x - ε)=0 and f(x + ε)≠0 +-- where ε=(max-min)/2^(nbtest+1) +maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => (a -> b) -> a -> a -> Int -> a -findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -findMaxOrdFor func minval maxval n = +maxZeroIndex _ minval maxval 0 = (minval+maxval)/2 +maxZeroIndex func minval maxval n = if func medpoint /= 0 - then findMaxOrdFor func minval medpoint (n-1) - else findMaxOrdFor func medpoint maxval (n-1) + then maxZeroIndex func minval medpoint (n-1) + else maxZeroIndex func medpoint maxval (n-1) where medpoint = (minval+maxval)/2 ymandel :: Point -> Point -> Point -> Point @@ -1554,9 +1614,36 @@ ymandel x y z = fromIntegral (mandel x y z 64) / 64 +And you can also consider small changes in other source files. + - [`YGL.hs`](code/06_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/06_Mandelbulb/Mandel.hs), the mandel function - [`ExtComplex`](code/06_Mandelbulb/ExtComplex.hs), the extended complexes Download the source code of this section → 06_Mandelbulb/Mandelbulb.lhs +## Conclusion + +As we can use imperative style in a functional language, +know you can use functional style in imperative languages. +This article exposed a way to organize some code in a functional way. +I'd like to stress the usage of Haskell made it very simple to achieve this. + +Once you are used to pure functional style, +it is hard not to see all advantages it offers. + +The code in the two last sections is completely pure and functional. +Furthermore I don't use `GLfloat`, `Color3` or any other OpenGL type. +If I want to use another library in the future, +I would be able to keep all the pure code and simply update the YGL module. + +The `YGL` module can be seen as a "wrapper" around 3D display and user interaction. +It is a clean separator between the imperative paradigm and functional paradigm. + +If you want to go further, it shouldn't be hard to add parallelism. +This should be easy mainly because most of the visible code is pure. +Such an optimization would have been harder by using directly the OpenGL library. + +You should also want to make a more precise object. Because, the Mandelbulb is +clearly not convex. But a precise rendering might be very long from +O(n².log(n)) to O(n³). diff --git a/output/Scratch/assets/css/main.css b/output/Scratch/assets/css/main.css index 9add234f1..91efec260 100644 --- a/output/Scratch/assets/css/main.css +++ b/output/Scratch/assets/css/main.css @@ -1 +1 @@ -@charset "UTF-8";@font-face{font-family:"cmunrm";src:url("fonts/cmunrm.eot");src:local("☺"),url("fonts/cmunrm.woff") format("woff"),url("fonts/cmunrm.ttf") format("truetype"),url("fonts/cmunrm.svg") format("svg");font-weight:400;font-style:normal}@font-face{font-family:"cmuntt";src:url("fonts/cmuntt.eot");src:local("☺"),url("fonts/cmuntt.woff") format("woff"),url("fonts/cmuntt.ttf") format("truetype"),url("fonts/cmuntt.svg") format("svg");font-weight:400;font-style:normal}@font-face{font-family:"cmunsl";src:url("fonts/cmunsl.eot");src:local("☺"),url("fonts/cmunsl.woff") format("woff"),url("fonts/cmunsl.ttf") format("truetype"),url("fonts/cmunsl. → svg") format("svg");font-weight:400;font-style:normal}@font-face{font-family:"cmunrb";src:url("fonts/cmunrb.eot");src:local("☺"),url("fonts/cmunrb.woff") format("woff"),url("fonts/cmunrb.ttf") format("truetype"),url("fonts/cmunrb.svg") format("svg");font-weight:400;font-style:normal}i,em{font-family:"cmunsl",Georgia,Palatino,"Century Schoolbook L","Times New Roman",Times,serif;font-style:normal}b,h1,h2,h3,h4,h5,h6{font-family:"cmunrb",Georgia,Palatino,"Century Schoolbook L","Times New Roman",Times,serif;font-weight:400}table.description tr td{border:1px solid #eeeef1}.assombris20{background-color:#eeeef1}body{color:#002b36;background-color:#fafafc}::selection{background:#002b36;color:#93a1a1}::-moz-selection{background:#002b36;color:#93a1a1}pre,code{font-family:"cmuntt",Incosolata,Monaco,monospace}pre::selection{background:#fdf6e3;color:#586e75}pre *::selection{background:#fdf6e3;color:#586e75}pre::-moz-selection{background:#fdf6e3;color:#586e75}pre *::-moz-selection{background:#fdf6e3;color:#586e75}a:hover{text-shadow:0 0 2px#faa}a,a:link,a:visited,a:active,a:hover{text-decoration:none;outline:none}a,a:link,a:visited,a:active{color:#002b36}a:hover{color:#cb4b16}hr{color:#eeeef1;border-top:1px solid #eeeef1;border-bottom:none;border-left:none;border-right:none}ul{list-style:square}ol,ul{padding-left:0}ol li,ul li{margin:.5em 0}ol li ul,ol li ol,ul li ol,ul li ul{margin:.5em 1.5em;list-style:circle}body,h1,h2,h3,h4,#entete,.tagname{text-rendering:optimizelegibility;line-height:1.4em}body{font-family:"CMU Serif","cmunrm",Georgia,Palatino,"Century Schoolbook L","Times New Roman",Times,serif}.article #afterheader{counter-reset:niv02}.article #afterheader h2{counter-increment:niv02;counter-reset:niv03;marker-offset:3em}.article #afterheader h2:before{content:counter(niv02) ". "}.article #afterheader h3{counter-increment:niv03;counter-reset:niv04}.article #afterheader h3:before{content:counter(niv02) "." counter(niv03) ". "}.article #afterheader h4{counter-increment:niv04}.article #afterheader h4:before{content:counter(niv02) "." counter(niv03) "." counter(niv04) ". "}pre{background-color:#002b36;color:#839496;box-shadow:0 0 5px #d0d0d2 inset;border-radius:3px;padding:1em;line-height:1.2em;font-size:.9em}p{margin-bottom:1.2em}blockquote{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;font-style:italic;padding:.5em 1em;color:#556}blockquote a:hover{color:#cb4b16}blockquote strong,blockquote b,blockquote i,blockquote em{font-weight:400;font-style:normal;color:#002b36}blockquote ul{padding-left:1.5em}abbr,acronym{font-variant:small-caps;text-decoration:none;border-bottom-width:0}#titre{letter-spacing:-0.06em;border-bottom:4px double #ccccd0;border-top:4px double #ccccd0}#liens .active,#sousliens{color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1}#liens .active a,#sousliens a{color:#667}#liens .active a:hover,#sousliens a:hover{color:#cb4b16}#liens .active a:hover strong,#liens .active a:hover b,#liens .active a:hover i,#liens .active a:hover em,#liens .active a:hover .nicer,#sousliens a:hover strong,#sousliens a:hover b,#sousliens a:hover i,#sousliens a:hover em,#sousliens a:hover .nicer{color:#ffb17c}#liens .active hr,#sousliens hr{color:#667;border-top:1px solid#667}#liens .active strong,#liens .active b,#liens .active i,#liens .active em,#sousliens strong,#sousliens b,#sousliens i,#sousliens em{color:#002b36}#liens a{border:1px solid#eee;background:rgba(0,0,0,0.05);box-shadow:0 0 2px white,0 0 3px#ccc inset;border:1px solid rgba(0,0,0,0.1);border-radius:3px}#liens a:hover{background:rgba(0,0,0,0.1);box-shadow:0 0 6px#555 inset}#liens .active{text-shadow:0 0 2px rgba(0,0,0,0.5);background-color:#f7f7f9;border:1px solid #e9e9eb;box-shadow:0 0 3px #c7c7c9 inset;border-radius:3px;border-top:none}#lastmod{font-size:.9em}.nojsbutton{font-size:2.5em}#clickcomment,#choixlang > a,#choixrss > a,.return > a{display:block;width:25%;cursor:pointer;margin:1em 0;padding:1em;font-size:16px;line-height:1.4em;border:1px solid #fafafc;color:#ccccd0}#clickcomment:hover,#choixlang > a:hover,#choixrss > a:hover,.return > a:hover{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;color:#dc5c27;text-shadow:0 0 2px#faa}#clickcomment:active,#choixlang > a:active,#choixrss > a:active,.return > a:active{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;color:#dc5c27;text-shadow:0 0 2px#faa;background:#f4f4f6}.return > a,#choixrss > a{float:right}#choix .return > a,#choix #choixrss > a{margin-top:0}.small{font-size:.8em}.sc{font-variant:small-caps}.impact,.darkimpact{font-size:2em;margin:0 auto 1em auto;line-height:1.3em}h1 > .date{font-size:.6em;color:#002b36}.date{font-size:.8em;color:#fafafc;border:1px solid #002b36;text-align:center;width:4.1em;line-height:1.5em;display:inline-block;vertical-align:middle;margin-right:1em}.date .day,.date .month,.date .year{display:block}.date .day{color:#002b36;background-color:#fafafc;float:left;width:1.7em}.date .month{float:right;width:2.3em;background-color:#002b36;color:#fafafc}.date .year{line-height:3ex;clear:both;color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1;border-radius:0}.date .year a{color:#667}.date .year a:hover{color:#cb4b16}.date .year a:hover strong,.date .year a:hover b,.date .year a:hover i,.date .year a:hover em,.date .year a:hover .nicer{color:#ffb17c}.date .year hr{color:#667;border-top:1px solid#667}.date .year strong,.date .year b,.date .year i,.date .year em{color:#002b36}body{text-align:center;font-size:16px}body > #entete{position:absolute;left:0;top:.5em;width:100%;min-width:50em;z-index:8000;padding-bottom:1em;margin-bottom:3em}#titre h2{width:80%;margin-left:auto;margin-right:auto;text-align:center;color:#ccccd0}#titre{text-align:center;width:100%}#titre h1,#titre h2{padding-left:1em;padding-right:1em}#bottom{clear:right;margin-right:0;padding:1.5em;line-height:1.5em;color:#224d58;margin-top:2em;text-align:center}#bottom a{color:#113c47}#bottom a:hover{color:#cb4b16}#sousliens{padding:1em 0;line-height:2em}#sousliens ul{list-style:none;margin-left:4em}ul.horizontal li{display:inline;font-size:.9em}ul.horizontal{margin-top:0;margin-bottom:0}#entete{padding-top:.1em;border-top:1px solid #ccccd0;border-bottom:1px solid #ccccd0}#liens{width:100%;padding:0;clear:both;margin-top:.5em}#liens ul{width:100%;clear:both;padding:0;margin:0}#liens ul li{display:inline-block;height:4em;margin-left:.2em;margin-right:.2em;width:23%}#liens ul li a,#liens ul li span{width:100%;display:block;line-height:4em}.clear{clear:both}#content{line-height:4em;margin-left:auto;margin-right:auto;margin-top:0;position:relative;clear:both;width:52em}.encadre,.black,.red,.intro,.resume,.shadow{padding:2em;margin-top:2em;margin-bottom:2em}.encadre,.black,.red,.shadow{color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1}.encadre a,.black a,.red a,.shadow a{color:#667}.encadre a:hover,.black a:hover,.red a:hover,.shadow a:hover{color:#cb4b16}.encadre a:hover strong,.encadre a:hover b,.encadre a:hover i,.encadre a:hover em,.encadre a:hover .nicer,.black a:hover strong,.black a:hover b,.black a:hover i,.black a:hover em,.black a:hover .nicer,.red a:hover strong,.red a:hover b,.red a:hover i,.red a:hover em,.red a:hover .nicer,.shadow a:hover strong,.shadow a:hover b,.shadow a:hover i,.shadow a:hover em,.shadow a:hover .nicer{color:#ffb17c}.encadre hr,.black hr,.red hr,.shadow hr{color:#667;border-top:1px solid#667}.encadre strong,.encadre b,.encadre i,.encadre em,.black strong,.black b,.black i,.black em,.red strong,.red b,.red i,.red em,.shadow strong,.shadow b,.shadow i,.shadow em{color:#002b36}pre .red{background:none;padding:0;margin:auto;border:none;box-shadow:none}.intro,.resume{font-size:.9em;font-style:italic;padding:.5em 1em;color:#556}.intro a:hover,.resume a:hover{color:#cb4b16}.intro strong,.intro b,.intro i,.intro em,.resume strong,.resume b,.resume i,.resume em{font-weight:400;font-style:normal;color:#002b36}#afterheader > h1{width:100%;padding-top:1.5em;text-align:left}#afterheader{padding-left:0;padding-right:0}#sousliens{margin-top:3em;margin-bottom:3em;font-size:1.2em;letter-spacing:1px;text-align:left;clear:both}.twilight{line-height:1.1em}.corps{font-size:1.25em;line-height:1.6em;text-align:justify;padding:3em 3em;margin:0;clear:both}.corps img{max-width:30em;border:1px solid #ccccd0;background-color:#fafafc;padding:.5em;box-shadow:0 10px 15px#ccc;border-radius:3px}.corps a:hover img{background-color:#dc3a05}figure{margin:3em 0}figure img{box-shadow:0 10px 15px#ccc inset}figure figcaption{text-align:center;margin:.5em 0}img.clean{border:none}#address{clear:both}.definitionCell{width:5em;vertical-align:top;font-weight:700;text-align:center}.valueCell{text-align:right}.smallblock{float:left;width:50%;font-size:1em;font-weight:700}.largeblock{float:right;width:70%;font-size:1em}#blackpage,#nojsredirect{top:0;left:0;width:100%;height:100%;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:fixed;text-align:center}#blackpage{color:#666;padding-top:10em;background-color:#eee;z-index:9000;cursor:wait}#blackpage img{background:none;border:none}#blackpage a{cursor:pointer}#nojsredirect{z-index:9001}.nojsbutton{width:50%;padding:1em;border:solid 3px white;margin-left:auto;margin-right:auto;margin-top:2em;z-index:9002}.codefile{font-size:.8em;text-align:right;padding-right:1em;margin-right:.1;margin-bottom:-1em}.flush{clear:both}table.description{border-spacing:5px;border-collapse:separate;margin-right:auto;margin-left:auto}table.description tr td{padding-left:.5em;padding-right:.5em;padding-top:.5ex;padding-bottom:.5ex;vertical-align:middle;margin-right:5px}ul.long li{margin-bottom:1em}img{display:block;margin:1.2em auto;background:none;border:none}img.left{float:left;max-width:30%;margin-top:.6em;margin-right:2em}img.inside{display:inline;vertical-align:middle}pre{overflow-x:auto;overflow-y:hidden}.navigationprev,.navigationnext{padding:0;margin-left:.2em;margin-right:.2em;margin-bottom:0;margin-top:3em;width:45%}.navigation .navigationprev,.navigation .navigationnext{width:30%;margin-top:0}.navigation{height:4em;border-bottom:#ccccd0 solid 1px}.presarticleleft,.presarticleright{font-size:1em}.navigationprev{float:left;text-align:left}.navigationnext{float:right;text-align:right}.impact,.darkimpact{text-align:left;width:66%;padding-left:.25em;padding-right:.25em}table.impact{text-align:left}table.impact tr td{padding-left:.25em;padding-right:.25em}#liens{font-size:1.2em}#iemessage{font-size:1.2em;color:#ccc;margin:-10px;padding:1px 0;background:#333}#iemessage strong,#iemessage b,#iemessage i,#iemessage em{color:#ccc}#iemessage a,#iemessage a:visited{color:#eca}.tagname{display:inline;cursor:pointer;margin-left:.5em;margin-right:.5em}.list{margin-top:3em}#menuMessage{font-size:1.2em;line-height:1.5em;width:100%;text-align:center}#next_before_articles{clear:both;width:100%;font-size:1.2em;padding-top:1em;padding-bottom:1em}#previous_articles,#next_articles{color:#889;font-style:italic;font-size:.8em}#previous_articles{float:left;margin-left:1em;width:45%;text-align:left}.previous_article,.next_article{margin-top:1em}#next_articles{float:right;width:45%;margin-right:1em;text-align:right}#rss{font-size:1.2em;text-align:center;display:block;width:100%;float:right;padding:1em .1em}.corps .return a{color:#eeeef1;padding:.1em;line-height:1.5em;font-size:1.5em;height:1.5em;float:left;font-size:2em;margin-top:-0.5em;margin-left:-2em;width:1.5em}a.return{color:#eeeef1;padding:.1em;line-height:1.5em;font-size:1.5em;height:1.5em;font-size:2em;width:1.5em;display:block}a.return:hover{color:#889}.corps .return a:hover{color:#cb4b16}.footnotes{font-size:.8em}.footnotes ol{color:#839496;font-weight:700}.footnotes ol p{color:#002b36;font-weight:400}.fontnotes ol{margin-left:0}.typeset img{display:inline;border:none;margin:0;padding:0}strong,b,i,em{font-weight:400;color:#889}strong a,b a,i a,em a{color:#002b36}strong a:hover,b a:hover,i a:hover,em a:hover{color:#cb4b16}.corps p strong,.corps p b,.corps p i,.corps p em{color:#556}a:hover strong,a:hover b,a:hover i,a:hover em{color:#dc5c27}a:hover .nicer{color:#ffb17c}.nicer{color:#ccccd0;font-family:"Lucida Grande",Tahoma}.block{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;width:26.5%;padding:1em;border-radius:2px;text-align:left;line-height:1em;margin-left:1%;margin-right:1%;font-size:.8em;height:9em}.block a{color:#002b36}.block a:hover{color:#cb4b16}.block h3{margin:0;font-size:1.3em}.block p{line-height:1.2em}.left{float:left}.right{float:right}.corps p a,.corps ul a{color:#556}.corps p a:hover,.corps ul a:hover{color:#cb4b16}ul.bloglist,.archive ul{list-style-type:none;margin:0}ul.bloglist li,.archive ul li{margin-bottom:1em}.button{cursor:pointer;text-align:center}#tagcloud{font-size:.8em;background:#f2f2f4;box-shadow:0 0 6px #ccccd0;border-radius:3px;line-height:2.5em;padding:2em;text-align:justify}.pala{font-family:Palatino}.article .corps a:after{content:"†";vertical-align:super;line-height:0;font-size:.66em;color:#889}.article .corps .footnotes a:after,.article .corps sup a:after{content:""}.article .corps sup a{font-weight:700;background:#839496;padding:0 .3em;margin-left:2px;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;color:#fafafc}.article .corps sup a:hover{background:#cb4b16}ul#markdown-toc,.intro .toc ul{font-variant:small-caps;list-style:none;padding-left:1.5em}ul#markdown-toc a:after,.intro .toc ul a:after{content:""}ul#markdown-toc ul ul,.intro .toc ul ul ul{font-variant:normal;line-height:1em;margin-bottom:1em}table{border:1px solid #ccccd0}table tr td{padding:2px .5em}table tr:nth-child(odd){background-color:#f2f2f4}table tr:nth-child(even){background-color:#fafafc}p pre code,ul li pre code,ol li pre code{background:none;border:none;padding:0}p code,ul li code,ol li code{background:#f0f0f2;border:solid 1px #ccccd0;padding:2px}ul.sameline{list-style:none}ul.sameline li{float:left;margin-left:.5em}.resumearticle{background-color:#f2f2f4;border-radius:7px;box-shadow:0 0 5px #c7c7b8 inset,0 0 5px white;margin:1em 0;padding:1em}a.cut{font-size:12px;font-family:Monaco,monospace;text-align:right;display:block;width:100%;opacity:.5;border:1px solid #fafafc;border-radius:3px}a.cut:hover{opacity:1;background-color:#f2f2f4;border-color:#ccccd0;box-shadow:0 0 3px #ccccd0 inset}a.cut strong{font-weight:700}.codehighlight pre{border-left:4px solid #ccccd0}#social{text-align:left;opacity:.3}#social:hover{opacity:1}.popularblock{width:30.333%;margin:0 1.5%;float:left}.popularblock figure{margin:0}.popularblock figure img{max-width:80%;max-height:6em} \ No newline at end of file +@charset "UTF-8";@font-face{font-family:"cmunrm";src:url("fonts/cmunrm.eot");src:local("☺"),url("fonts/cmunrm.ttf") format("truetype"),url("fonts/cmunrm.svg") format("svg");font-weight:400;font-style:normal}@font-face{font-family:"cmuntt";src:url("fonts/cmuntt.eot");src:local("☺"),url("fonts/cmuntt.ttf") format("truetype"),url("fonts/cmuntt.svg") format("svg");font-weight:400;font-style:normal}@font-face{font-family:"cmunsl";src:url("fonts/cmunsl.eot");src:local("☺"),url("fonts/cmunsl.ttf") format("truetype"),url("fonts/cmunsl. → svg") format("svg");font-weight:400;font-style:normal}@font-face{font-family:"cmunrb";src:url("fonts/cmunrb.eot");src:local("☺"),url("fonts/cmunrb.ttf") format("truetype"),url("fonts/cmunrb.svg") format("svg");font-weight:400;font-style:normal}i,em{font-family:"cmunsl",Georgia,Palatino,"Century Schoolbook L","Times New Roman",Times,serif;font-style:normal}b,h1,h2,h3,h4,h5,h6{font-family:"cmunrb",Georgia,Palatino,"Century Schoolbook L","Times New Roman",Times,serif;font-weight:400}table.description tr td{border:1px solid #eeeef1}.assombris20{background-color:#eeeef1}body{color:#002b36;background-color:#fafafc}::selection{background:#002b36;color:#93a1a1}::-moz-selection{background:#002b36;color:#93a1a1}pre,code{font-family:"cmuntt",Incosolata,Monaco,monospace}pre::selection{background:#fdf6e3;color:#586e75}pre *::selection{background:#fdf6e3;color:#586e75}pre::-moz-selection{background:#fdf6e3;color:#586e75}pre *::-moz-selection{background:#fdf6e3;color:#586e75}a:hover{text-shadow:0 0 2px#faa}a,a:link,a:visited,a:active,a:hover{text-decoration:none;outline:none}a,a:link,a:visited,a:active{color:#002b36}a:hover{color:#cb4b16}hr{color:#eeeef1;border-top:1px solid #eeeef1;border-bottom:none;border-left:none;border-right:none}ul{list-style:square}ol,ul{padding-left:0}ol li,ul li{margin:.5em 0}ol li ul,ol li ol,ul li ol,ul li ul{margin:.5em 1.5em;list-style:circle}body,h1,h2,h3,h4,#entete,.tagname{text-rendering:optimizelegibility;line-height:1.4em}body{font-family:"CMU Serif","cmunrm",Georgia,Palatino,"Century Schoolbook L","Times New Roman",Times,serif}.article #afterheader{counter-reset:niv02}.article #afterheader h2{counter-increment:niv02;counter-reset:niv03;marker-offset:3em}.article #afterheader h2:before{content:counter(niv02) ". "}.article #afterheader h3{counter-increment:niv03;counter-reset:niv04}.article #afterheader h3:before{content:counter(niv02) "." counter(niv03) ". "}.article #afterheader h4{counter-increment:niv04}.article #afterheader h4:before{content:counter(niv02) "." counter(niv03) "." counter(niv04) ". "}pre{background-color:#002b36;color:#839496;box-shadow:0 0 5px #d0d0d2 inset;border-radius:3px;padding:1em;line-height:1.2em;font-size:.9em}p{margin-bottom:1.2em}blockquote{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;font-style:italic;padding:.5em 1em;color:#556}blockquote a:hover{color:#cb4b16}blockquote strong,blockquote b,blockquote i,blockquote em{font-weight:400;font-style:normal;color:#002b36}blockquote ul{padding-left:1.5em}abbr,acronym{font-variant:small-caps;text-decoration:none;border-bottom-width:0}#titre{letter-spacing:-0.06em;border-bottom:4px double #ccccd0;border-top:4px double #ccccd0}#liens .active,#sousliens{color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1}#liens .active a,#sousliens a{color:#667}#liens .active a:hover,#sousliens a:hover{color:#cb4b16}#liens .active a:hover strong,#liens .active a:hover b,#liens .active a:hover i,#liens .active a:hover em,#liens .active a:hover .nicer,#sousliens a:hover strong,#sousliens a:hover b,#sousliens a:hover i,#sousliens a:hover em,#sousliens a:hover .nicer{color:#ffb17c}#liens .active hr,#sousliens hr{color:#667;border-top:1px solid#667}#liens .active strong,#liens .active b,#liens .active i,#liens .active em,#sousliens strong,#sousliens b,#sousliens i,#sousliens em{color:#002b36}#liens a{border:1px solid#eee;background:rgba(0,0,0,0.05);box-shadow:0 0 2px white,0 0 3px#ccc inset;border:1px solid rgba(0,0,0,0.1);border-radius:3px}#liens a:hover{background:rgba(0,0,0,0.1);box-shadow:0 0 6px#555 inset}#liens .active{text-shadow:0 0 2px rgba(0,0,0,0.5);background-color:#f7f7f9;border:1px solid #e9e9eb;box-shadow:0 0 3px #c7c7c9 inset;border-radius:3px;border-top:none}#lastmod{font-size:.9em}.nojsbutton{font-size:2.5em}#clickcomment,#choixlang > a,#choixrss > a,.return > a{display:block;width:25%;cursor:pointer;margin:1em 0;padding:1em;font-size:16px;line-height:1.4em;border:1px solid #fafafc;color:#ccccd0}#clickcomment:hover,#choixlang > a:hover,#choixrss > a:hover,.return > a:hover{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;color:#dc5c27;text-shadow:0 0 2px#faa}#clickcomment:active,#choixlang > a:active,#choixrss > a:active,.return > a:active{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;color:#dc5c27;text-shadow:0 0 2px#faa;background:#f4f4f6}.return > a,#choixrss > a{float:right}#choix .return > a,#choix #choixrss > a{margin-top:0}.small{font-size:.8em}.sc{font-variant:small-caps}.impact,.darkimpact{font-size:2em;margin:0 auto 1em auto;line-height:1.3em}h1 > .date{font-size:.6em;color:#002b36}.date{font-size:.8em;color:#fafafc;border:1px solid #002b36;text-align:center;width:4.1em;line-height:1.5em;display:inline-block;vertical-align:middle;margin-right:1em}.date .day,.date .month,.date .year{display:block}.date .day{color:#002b36;background-color:#fafafc;float:left;width:1.7em}.date .month{float:right;width:2.3em;background-color:#002b36;color:#fafafc}.date .year{line-height:3ex;clear:both;color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1;border-radius:0}.date .year a{color:#667}.date .year a:hover{color:#cb4b16}.date .year a:hover strong,.date .year a:hover b,.date .year a:hover i,.date .year a:hover em,.date .year a:hover .nicer{color:#ffb17c}.date .year hr{color:#667;border-top:1px solid#667}.date .year strong,.date .year b,.date .year i,.date .year em{color:#002b36}body{text-align:center;font-size:16px}body > #entete{position:absolute;left:0;top:.5em;width:100%;min-width:50em;z-index:8000;padding-bottom:1em;margin-bottom:3em}#titre h2{width:80%;margin-left:auto;margin-right:auto;text-align:center;color:#ccccd0}#titre{text-align:center;width:100%}#titre h1,#titre h2{padding-left:1em;padding-right:1em}#bottom{clear:right;margin-right:0;padding:1.5em;line-height:1.5em;color:#224d58;margin-top:2em;text-align:center}#bottom a{color:#113c47}#bottom a:hover{color:#cb4b16}#sousliens{padding:1em 0;line-height:2em}#sousliens ul{list-style:none;margin-left:4em}ul.horizontal li{display:inline;font-size:.9em}ul.horizontal{margin-top:0;margin-bottom:0}#entete{padding-top:.1em;border-top:1px solid #ccccd0;border-bottom:1px solid #ccccd0}#liens{width:100%;padding:0;clear:both;margin-top:.5em}#liens ul{width:100%;clear:both;padding:0;margin:0}#liens ul li{display:inline-block;height:4em;margin-left:.2em;margin-right:.2em;width:23%}#liens ul li a,#liens ul li span{width:100%;display:block;line-height:4em}.clear{clear:both}#content{line-height:4em;margin-left:auto;margin-right:auto;margin-top:0;position:relative;clear:both;width:52em}.encadre,.black,.red,.intro,.resume,.shadow{padding:2em;margin-top:2em;margin-bottom:2em}.encadre,.black,.red,.shadow{color:#002b36;border:#ccccd0 solid 1px;border-radius:5px;box-shadow:0 0 2px #ccccd0 inset;background-color:#eeeef1}.encadre a,.black a,.red a,.shadow a{color:#667}.encadre a:hover,.black a:hover,.red a:hover,.shadow a:hover{color:#cb4b16}.encadre a:hover strong,.encadre a:hover b,.encadre a:hover i,.encadre a:hover em,.encadre a:hover .nicer,.black a:hover strong,.black a:hover b,.black a:hover i,.black a:hover em,.black a:hover .nicer,.red a:hover strong,.red a:hover b,.red a:hover i,.red a:hover em,.red a:hover .nicer,.shadow a:hover strong,.shadow a:hover b,.shadow a:hover i,.shadow a:hover em,.shadow a:hover .nicer{color:#ffb17c}.encadre hr,.black hr,.red hr,.shadow hr{color:#667;border-top:1px solid#667}.encadre strong,.encadre b,.encadre i,.encadre em,.black strong,.black b,.black i,.black em,.red strong,.red b,.red i,.red em,.shadow strong,.shadow b,.shadow i,.shadow em{color:#002b36}pre .red{background:none;padding:0;margin:auto;border:none;box-shadow:none}.intro,.resume{font-size:.9em;font-style:italic;padding:.5em 1em;color:#556}.intro a:hover,.resume a:hover{color:#cb4b16}.intro strong,.intro b,.intro i,.intro em,.resume strong,.resume b,.resume i,.resume em{font-weight:400;font-style:normal;color:#002b36}#afterheader > h1{width:100%;padding-top:1.5em;text-align:left}#afterheader{padding-left:0;padding-right:0}#sousliens{margin-top:3em;margin-bottom:3em;font-size:1.2em;letter-spacing:1px;text-align:left;clear:both}.twilight{line-height:1.1em}.corps{font-size:1.25em;line-height:1.6em;text-align:justify;padding:3em 3em;margin:0;clear:both}.corps img{max-width:30em;border:1px solid #ccccd0;background-color:#fafafc;padding:.5em;box-shadow:0 10px 15px#ccc;border-radius:3px}.corps a:hover img{background-color:#dc3a05}figure{margin:3em 0}figure img{box-shadow:0 10px 15px#ccc inset}figure figcaption{text-align:center;margin:.5em 0}img.clean{border:none}#address{clear:both}.definitionCell{width:5em;vertical-align:top;font-weight:700;text-align:center}.valueCell{text-align:right}.smallblock{float:left;width:50%;font-size:1em;font-weight:700}.largeblock{float:right;width:70%;font-size:1em}#blackpage,#nojsredirect{top:0;left:0;width:100%;height:100%;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:fixed;text-align:center}#blackpage{color:#666;padding-top:10em;background-color:#eee;z-index:9000;cursor:wait}#blackpage img{background:none;border:none}#blackpage a{cursor:pointer}#nojsredirect{z-index:9001}.nojsbutton{width:50%;padding:1em;border:solid 3px white;margin-left:auto;margin-right:auto;margin-top:2em;z-index:9002}.codefile{font-size:.8em;text-align:right;padding-right:1em;margin-right:.1;margin-bottom:-1em}.flush{clear:both}table.description{border-spacing:5px;border-collapse:separate;margin-right:auto;margin-left:auto}table.description tr td{padding-left:.5em;padding-right:.5em;padding-top:.5ex;padding-bottom:.5ex;vertical-align:middle;margin-right:5px}ul.long li{margin-bottom:1em}img{display:block;margin:1.2em auto;background:none;border:none}img.left{float:left;max-width:30%;margin-top:.6em;margin-right:2em}img.inside{display:inline;vertical-align:middle}pre{overflow-x:auto;overflow-y:hidden}.navigationprev,.navigationnext{padding:0;margin-left:.2em;margin-right:.2em;margin-bottom:0;margin-top:3em;width:45%}.navigation .navigationprev,.navigation .navigationnext{width:30%;margin-top:0}.navigation{height:4em;border-bottom:#ccccd0 solid 1px}.presarticleleft,.presarticleright{font-size:1em}.navigationprev{float:left;text-align:left}.navigationnext{float:right;text-align:right}.impact,.darkimpact{text-align:left;width:66%;padding-left:.25em;padding-right:.25em}table.impact{text-align:left}table.impact tr td{padding-left:.25em;padding-right:.25em}#liens{font-size:1.2em}#iemessage{font-size:1.2em;color:#ccc;margin:-10px;padding:1px 0;background:#333}#iemessage strong,#iemessage b,#iemessage i,#iemessage em{color:#ccc}#iemessage a,#iemessage a:visited{color:#eca}.tagname{display:inline;cursor:pointer;margin-left:.5em;margin-right:.5em}.list{margin-top:3em}#menuMessage{font-size:1.2em;line-height:1.5em;width:100%;text-align:center}#next_before_articles{clear:both;width:100%;font-size:1.2em;padding-top:1em;padding-bottom:1em}#previous_articles,#next_articles{color:#889;font-style:italic;font-size:.8em}#previous_articles{float:left;margin-left:1em;width:45%;text-align:left}.previous_article,.next_article{margin-top:1em}#next_articles{float:right;width:45%;margin-right:1em;text-align:right}#rss{font-size:1.2em;text-align:center;display:block;width:100%;float:right;padding:1em .1em}.corps .return a{color:#eeeef1;padding:.1em;line-height:1.5em;font-size:1.5em;height:1.5em;float:left;font-size:2em;margin-top:-0.5em;margin-left:-2em;width:1.5em}a.return{color:#eeeef1;padding:.1em;line-height:1.5em;font-size:1.5em;height:1.5em;font-size:2em;width:1.5em;display:block}a.return:hover{color:#889}.corps .return a:hover{color:#cb4b16}.footnotes{font-size:.8em}.footnotes ol{color:#839496;font-weight:700}.footnotes ol p{color:#002b36;font-weight:400}.fontnotes ol{margin-left:0}.typeset img{display:inline;border:none;margin:0;padding:0}strong,b,i,em{font-weight:400;color:#889}strong a,b a,i a,em a{color:#002b36}strong a:hover,b a:hover,i a:hover,em a:hover{color:#cb4b16}.corps p strong,.corps p b,.corps p i,.corps p em{color:#556}a:hover strong,a:hover b,a:hover i,a:hover em{color:#dc5c27}a:hover .nicer{color:#ffb17c}.nicer{color:#ccccd0;font-family:"Lucida Grande",Tahoma}.block{border:solid 1px #ccccd0;border-radius:2px;box-shadow:0 0 4px #f2f2f4 inset;background-color:#f8f8fa;width:26.5%;padding:1em;border-radius:2px;text-align:left;line-height:1em;margin-left:1%;margin-right:1%;font-size:.8em;height:9em}.block a{color:#002b36}.block a:hover{color:#cb4b16}.block h3{margin:0;font-size:1.3em}.block p{line-height:1.2em}.left{float:left}.right{float:right}.corps p a,.corps ul a{color:#556}.corps p a:hover,.corps ul a:hover{color:#cb4b16}ul.bloglist,.archive ul{list-style-type:none;margin:0}ul.bloglist li,.archive ul li{margin-bottom:1em}.button{cursor:pointer;text-align:center}#tagcloud{font-size:.8em;background:#f2f2f4;box-shadow:0 0 6px #ccccd0;border-radius:3px;line-height:2.5em;padding:2em;text-align:justify}.pala{font-family:Palatino}.article .corps a:after{content:"†";vertical-align:super;line-height:0;font-size:.66em;color:#889}.article .corps .footnotes a:after,.article .corps sup a:after{content:""}.article .corps sup a{font-weight:700;background:#839496;padding:0 .3em;margin-left:2px;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;color:#fafafc}.article .corps sup a:hover{background:#cb4b16}ul#markdown-toc,.intro .toc ul{font-variant:small-caps;list-style:none;padding-left:1.5em}ul#markdown-toc a:after,.intro .toc ul a:after{content:""}ul#markdown-toc ul ul,.intro .toc ul ul ul{font-variant:normal;line-height:1em;margin-bottom:1em}table{border:1px solid #ccccd0}table tr td{padding:2px .5em}table tr:nth-child(odd){background-color:#f2f2f4}table tr:nth-child(even){background-color:#fafafc}p pre code,ul li pre code,ol li pre code{background:none;border:none;padding:0}p code,ul li code,ol li code{background:#f0f0f2;border:solid 1px #ccccd0;padding:2px}ul.sameline{list-style:none}ul.sameline li{float:left;margin-left:.5em}.resumearticle{background-color:#f2f2f4;border-radius:7px;box-shadow:0 0 5px #c7c7b8 inset,0 0 5px white;margin:1em 0;padding:1em}a.cut{font-size:12px;font-family:Monaco,monospace;text-align:right;display:block;width:100%;opacity:.5;border:1px solid #fafafc;border-radius:3px}a.cut:hover{opacity:1;background-color:#f2f2f4;border-color:#ccccd0;box-shadow:0 0 3px #ccccd0 inset}a.cut strong{font-weight:700}.codehighlight pre{border-left:4px solid #ccccd0}#social{text-align:left;opacity:.3}#social:hover{opacity:1}.popularblock{width:30.333%;margin:0 1.5%;float:left}.popularblock figure{margin:0}.popularblock figure img{max-width:80%;max-height:6em} \ No newline at end of file diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs index 1075c6848..765c3960b 100644 --- a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs @@ -39,11 +39,13 @@ and something nice to see in 3D. Here is what you'll end with: -blogimage("GoldenMandelbulb.png","A golden mandelbulb") +blogfigure("GoldenMandelbulb.png","The entire Mandelbulb") +blogfigure("3DMandelbulbDetail.png","A Mandelbulb detail") +blogfigure("3DMandelbulbDetail2.png","Another detail of the Mandelbulb") And here are the intermediate steps: blogimage("HGL_Plan.png","The parts of the article") -From 1 to 3 it will be _dirtier_ and _dirtier_. -We start cleaning everything at the 4th part. +From the 2nd section to the 4th it will be _dirtier_ and _dirtier_. +We start cleaning everything at the 5th section. diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs index 41b653162..f8f5d69df 100644 --- a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs @@ -140,7 +140,7 @@ Given two coordinates in pixels, it returns some integer value: > f (complex r i) 0 64 It uses the main Mandelbrot function for each complex \\(c\\). -The Mandelbrot set is the set of complex number c such that the following sequence does not escape to infinity. +The Mandelbrot set is the set of complex number \\(c\\) such that the following sequence does not escape to infinity. Let us define \\(f_c: \mathbb{C} \to \mathbb{C}\\) diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs index b56a1f932..f292b99a7 100644 --- a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs @@ -55,7 +55,7 @@ The method I use is a rough approximation. I consider the Mandelbrot set to be almost convex. The result will be good enough. -We change slightly the drawMandelbrot function. +We change slightly the `drawMandelbrot` function. We replace the `Points` by `LineLoop` > drawMandelbrot = @@ -77,12 +77,12 @@ we will choose only point on the surface. > map (\(x,y,c) -> (x,-y,c)) (reverse positivePoints) We only need to compute the positive point. -The Mandelbrot set is symmetric on the abscisse axis. +The Mandelbrot set is symmetric relatively to the abscisse axis. > positivePoints :: [(GLfloat,GLfloat,Color3 GLfloat)] > positivePoints = do > x <- [-width..width] -> let y = findMaxOrdFor (mandel x) 0 height (log2 height) +> let y = maxZeroIndex (mandel x) 0 height (log2 height) > if y < 1 -- We don't draw point in the absciss > then [] > else return (x/width,y/height,colorFromValue $ mandel x y) @@ -92,22 +92,30 @@ The Mandelbrot set is symmetric on the abscisse axis. This function is interesting. For those not used to the list monad here is a natural language version of this function: -~~~ + positivePoints = for all x in the range [-width..width] let y be smallest number s.t. mandel x y > 0 if y is on 0 then don't return a point else return the value corresonding to (x,y,color for (x+iy)) -~~~ + In fact using the list monad you write like if you consider only one element at a time and the computation is done non deterministically. To find the smallest number such that `mandel x y > 0` we use a simple dichotomy: -> findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = > if (func medpoint) /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 No rocket science here. See the result now: diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs index 59af425db..ad9026168 100644 --- a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs @@ -10,7 +10,7 @@ But it will be enough for us to create something that look nice. This section is quite long, but don't be afraid, most of the code is some OpenGL boilerplate. -For those you want to skim, +If you just want to skim this section, here is a high level representation: > - OpenGL Boilerplate @@ -263,7 +263,7 @@ depthPoints = do x <- [-width..width] y <- [-height..height] let - depthOf x' y' = findMaxOrdFor (mandel x' y') 0 deep logdeep + depthOf x' y' = maxZeroIndex (mandel x' y') 0 deep logdeep logdeep = floor ((log deep) / log 2) z1 = depthOf x y z2 = depthOf (x+1) y @@ -292,7 +292,7 @@ Here is a harder to read but shorter and more generic rewritten function: > y <- [-height..height] > let > neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] -> depthOf (u,v) = findMaxOrdFor (mandel u v) 0 deep logdeep +> depthOf (u,v) = maxZeroIndex (mandel u v) 0 deep logdeep > logdeep = floor ((log deep) / log 2) > -- zs are 3D points with found depth > zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors @@ -324,11 +324,21 @@ The rest of the program is very close to the preceding one.
-> findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => +> (a -> b) -> a -> a -> Int -> a +> maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = > if (func medpoint) /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 I made the color slightly brighter diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs index 270eab67b..9bad8ecfd 100644 --- a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs @@ -42,7 +42,7 @@ This is similar to the preceding section. > y <- [0..height] > let > neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] -> depthOf (u,v) = findMaxOrdFor (ymandel u v) 0 deep 7 +> depthOf (u,v) = maxZeroIndex (ymandel u v) 0 deep 7 > -- zs are 3D points with found depth > zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors > -- ts are 3D pixels + mandel value @@ -56,11 +56,20 @@ This is similar to the preceding section. > -- Draw two triangles > else [ps!!0,ps!!1,ps!!2,ps!!0,ps!!2,ps!!3] > -> findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = +> +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = > if (func medpoint) /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 > > colorFromValue n = diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs index 08fe85c54..7bba54b81 100644 --- a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs @@ -2,21 +2,26 @@ Some points: -1. OpenGL and GLUT are linked to the C library. +1. OpenGL and GLUT is done in C. In particular the `mainLoop` function is a direct link to the C library (FFI). - This function if so far from the pure spirit of functional languages. + This function is clearly far from the functional paradigm. Could we make this better? - We will have two choices, or create our own `mainLoop` function to make it more functional. - Or deal with the imperative nature of the GLUT `mainLoop` function. - As a goal of this article is to understand how to deal with existing library and particularly the one coming from imperative language we will continue to use the `mainLoop` function. -2. Or main problem come from user interaction. - If you ask the Internet, about how to deal with user interaction with a functional paradigm, the main answer is to use _functional reactive programming_ (FRP). - I read very few about FRP, and I might be completely wrong when I say that it is about creating a DSL where atoms are time functions. - While I'm writing these lines, I don't know if I'll do something looking close to that. - For now I'll simply try to resolve the first problem. + We will have two choices: -Then here is how I imagine things should go. -First, what the main loop should look like: + - create our own `mainLoop` function to make it more functional. + - deal with the imperative nature of the GLUT `mainLoop` function. + + As one of the goal of this article is to understand how to deal with existing libraries and particularly the one coming from imperative languages, we will continue to use the `mainLoop` function. +2. Our main problem come from user interaction. + If you ask "the Internet", + about how to deal with user interaction with a functional paradigm, + the main answer is to use _functional reactive programming_ (FRP). + I won't use FRP in this article. + Instead, I'll use a simpler while less effective way to deal with user interaction. + But The method I'll use will be as pure and functional as possible. + +Here is how I imagine things should go. +First, what the main loop should look like if we could make our own: functionalMainLoop = @@ -89,6 +94,14 @@ We simply have to provide the definition of some functions. > camPos = position w, > camDir = angle w, > camZoom = scale w } +> -- objects for world w +> -- is the list of one unique element +> -- The element is an YObject +> -- more precisely the XYFunc Function3D Box3D +> -- where the Function3D is the type +> -- Point -> Point -> Maybe (Point,Color) +> -- and its value here is ((shape w) res) +> -- and the Box3D value is defbox > objects w = [XYFunc ((shape w) res) defbox] > where > res = resolution $ box w @@ -110,8 +123,8 @@ These function are used to update the world state. > zdir :: Point3D > zdir = makePoint3D (0,0,1) -Note `(-*<)` is scalar product. -Also note we could add Point3D as numbers. +Note `(-*<)` is the scalar product (`α -*< (x,y,z) = (αx,αy,αz)`). +Also note we could add two Point3D. > rotate :: Point3D -> Scalar -> World -> World > rotate dir angleValue world = @@ -188,9 +201,9 @@ Because we consider partial functions > shapeFunc :: Scalar -> Function3D > shapeFunc res x y = > let -> z = findMaxOrdFor (ymandel x y) 0 1 20 +> z = maxZeroIndex (ymandel x y) 0 1 20 > in -> if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | +> if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | > val <- [res], xeps <- [-val,val], yeps<-[-val,val]] > then Nothing > else Just (z,colorFromValue ((ymandel x y z) * 64)) @@ -207,21 +220,29 @@ With the color function. The rest is similar to the preceding sections. -> findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => > (a -> b) -> a -> a -> Int -> a -> findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = -> if func medpoint /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = +> if (func medpoint) /= 0 +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 > > ymandel :: Point -> Point -> Point -> Point > ymandel x y z = fromIntegral (mandel x y z 64) / 64 -I won't put how the magic occurs directly here. -But all the magic occurs in the file `YGL.hs`. -This file is commented a lot. +I won't explain how the magic occurs here. +If you are interested, just read the file [`YGL.hs`](code/05_Mandelbulb/YGL.hs). +It is commented a lot. - [`YGL.hs`](code/05_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/05_Mandelbulb/Mandel.hs), the mandel function diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs index b1b21dde4..5fa20a0d2 100644 --- a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs @@ -1,6 +1,3 @@ --- The languages include needed because I wanted to use --- (Point,Point,Point) instead of --- data Point3D = Point3D (Point,Point,Point) deriving ... {- The module YGL will contains most boilerplate And display details. @@ -37,11 +34,20 @@ module YGL ( -- The main loop function to call , yMainLoop) where -import Numeric (readHex) +-- A bunch of imports +import Numeric (readHex) -- to read hexadecimal values + +-- Import of OpenGL and GLUT +-- but, I use my own Color type, therefore I hide the definition +-- of Color inside GLUT and OpenGL packages import Graphics.Rendering.OpenGL hiding (Color) import Graphics.UI.GLUT hiding (Color) import Data.IORef + +-- I use Map to deal with user interaction import qualified Data.Map as Map + +-- Some standard stuff import Control.Monad (when) import Data.Maybe (isNothing) @@ -49,6 +55,7 @@ import Data.Maybe (isNothing) - Just take the time to follow me. --} + -- | A 1D point type Point = GLfloat -- | A Scalar value diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs index d8d7c9793..1b05472c5 100644 --- a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs @@ -1,7 +1,8 @@ ## Optimization From the architecture stand point all is clear. -If you read the code, you'll see I didn't made everything perfect, for example, I didn't coded nicely the lights. +If you read the code of `YGL.hs`, you'll see I didn't made everything perfect. +For example, I didn't finished the code of the lights. But I believe it is a good first step and it will be easy to go further. The separation between rendering and world behavior is clear. Unfortunately the program of the preceding session is extremely slow. @@ -124,13 +125,14 @@ Our initial world state is slightly changed: > , anglePerSec = 5.0 > , position = makePoint3D (0,0,0) > , scale = 1.0 -> , box = Box3D { minPoint = makePoint3D (-2,-2,-2) -> , maxPoint = makePoint3D (2,2,2) -> , resolution = 0.03 } +> , box = Box3D { minPoint = makePoint3D (0-eps, 0-eps, 0-eps) +> , maxPoint = makePoint3D (0+eps, 0+eps, 0+eps) +> , resolution = 0.02 } > , told = 0 > -- We declare cache directly this time > , cache = objectFunctionFromWorld initialWorld > } +> where eps=2 We use the `YGL.getObject3DFromShapeFunction` function directly. This way instead of providing `XYFunc`, we provide directly a list of Atoms. @@ -183,9 +185,9 @@ All the rest is exactly the same. > shapeFunc :: Scalar -> Function3D > shapeFunc res x y = > let -> z = findMaxOrdFor (ymandel x y) 0 1 20 +> z = maxZeroIndex (ymandel x y) 0 1 20 > in -> if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | +> if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | > val <- [res], xeps <- [-val,val], yeps<-[-val,val]] > then Nothing > else Just (z,colorFromValue 0) @@ -198,13 +200,21 @@ All the rest is exactly the same. > in > makeColor (t n) (t (n+5)) (t (n+10)) > -> findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => > (a -> b) -> a -> a -> Int -> a -> findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = +> maxZeroIndex _ minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = > if func medpoint /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 > > ymandel :: Point -> Point -> Point -> Point @@ -212,6 +222,8 @@ All the rest is exactly the same.
+And you can also consider small changes in other source files. + - [`YGL.hs`](code/06_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/06_Mandelbulb/Mandel.hs), the mandel function - [`ExtComplex`](code/06_Mandelbulb/ExtComplex.hs), the extended complexes diff --git a/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs new file mode 100644 index 000000000..d222d3f9e --- /dev/null +++ b/output/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs @@ -0,0 +1,25 @@ + ## Conclusion + +As we can use imperative style in a functional language, +know you can use functional style in imperative languages. +This article exposed a way to organize some code in a functional way. +I'd like to stress the usage of Haskell made it very simple to achieve this. + +Once you are used to pure functional style, +it is hard not to see all advantages it offers. + +The code in the two last sections is completely pure and functional. +Furthermore I don't use `GLfloat`, `Color3` or any other OpenGL type. +If I want to use another library in the future, +I would be able to keep all the pure code and simply update the YGL module. + +The `YGL` module can be seen as a "wrapper" around 3D display and user interaction. +It is a clean separator between the imperative paradigm and functional paradigm. + +If you want to go further, it shouldn't be hard to add parallelism. +This should be easy mainly because most of the visible code is pure. +Such an optimization would have been harder by using directly the OpenGL library. + +You should also want to make a more precise object. Because, the Mandelbulb is +clearly not convex. But a precise rendering might be very long from +O(n².log(n)) to O(n³). diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs index 1075c6848..765c3960b 100644 --- a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs @@ -39,11 +39,13 @@ and something nice to see in 3D. Here is what you'll end with: -blogimage("GoldenMandelbulb.png","A golden mandelbulb") +blogfigure("GoldenMandelbulb.png","The entire Mandelbulb") +blogfigure("3DMandelbulbDetail.png","A Mandelbulb detail") +blogfigure("3DMandelbulbDetail2.png","Another detail of the Mandelbulb") And here are the intermediate steps: blogimage("HGL_Plan.png","The parts of the article") -From 1 to 3 it will be _dirtier_ and _dirtier_. -We start cleaning everything at the 4th part. +From the 2nd section to the 4th it will be _dirtier_ and _dirtier_. +We start cleaning everything at the 5th section. diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs index 41b653162..f8f5d69df 100644 --- a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs @@ -140,7 +140,7 @@ Given two coordinates in pixels, it returns some integer value: > f (complex r i) 0 64 It uses the main Mandelbrot function for each complex \\(c\\). -The Mandelbrot set is the set of complex number c such that the following sequence does not escape to infinity. +The Mandelbrot set is the set of complex number \\(c\\) such that the following sequence does not escape to infinity. Let us define \\(f_c: \mathbb{C} \to \mathbb{C}\\) diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs index b56a1f932..f292b99a7 100644 --- a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs @@ -55,7 +55,7 @@ The method I use is a rough approximation. I consider the Mandelbrot set to be almost convex. The result will be good enough. -We change slightly the drawMandelbrot function. +We change slightly the `drawMandelbrot` function. We replace the `Points` by `LineLoop` > drawMandelbrot = @@ -77,12 +77,12 @@ we will choose only point on the surface. > map (\(x,y,c) -> (x,-y,c)) (reverse positivePoints) We only need to compute the positive point. -The Mandelbrot set is symmetric on the abscisse axis. +The Mandelbrot set is symmetric relatively to the abscisse axis. > positivePoints :: [(GLfloat,GLfloat,Color3 GLfloat)] > positivePoints = do > x <- [-width..width] -> let y = findMaxOrdFor (mandel x) 0 height (log2 height) +> let y = maxZeroIndex (mandel x) 0 height (log2 height) > if y < 1 -- We don't draw point in the absciss > then [] > else return (x/width,y/height,colorFromValue $ mandel x y) @@ -92,22 +92,30 @@ The Mandelbrot set is symmetric on the abscisse axis. This function is interesting. For those not used to the list monad here is a natural language version of this function: -~~~ + positivePoints = for all x in the range [-width..width] let y be smallest number s.t. mandel x y > 0 if y is on 0 then don't return a point else return the value corresonding to (x,y,color for (x+iy)) -~~~ + In fact using the list monad you write like if you consider only one element at a time and the computation is done non deterministically. To find the smallest number such that `mandel x y > 0` we use a simple dichotomy: -> findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = > if (func medpoint) /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 No rocket science here. See the result now: diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs index 59af425db..ad9026168 100644 --- a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs @@ -10,7 +10,7 @@ But it will be enough for us to create something that look nice. This section is quite long, but don't be afraid, most of the code is some OpenGL boilerplate. -For those you want to skim, +If you just want to skim this section, here is a high level representation: > - OpenGL Boilerplate @@ -263,7 +263,7 @@ depthPoints = do x <- [-width..width] y <- [-height..height] let - depthOf x' y' = findMaxOrdFor (mandel x' y') 0 deep logdeep + depthOf x' y' = maxZeroIndex (mandel x' y') 0 deep logdeep logdeep = floor ((log deep) / log 2) z1 = depthOf x y z2 = depthOf (x+1) y @@ -292,7 +292,7 @@ Here is a harder to read but shorter and more generic rewritten function: > y <- [-height..height] > let > neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] -> depthOf (u,v) = findMaxOrdFor (mandel u v) 0 deep logdeep +> depthOf (u,v) = maxZeroIndex (mandel u v) 0 deep logdeep > logdeep = floor ((log deep) / log 2) > -- zs are 3D points with found depth > zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors @@ -324,11 +324,21 @@ The rest of the program is very close to the preceding one.
-> findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => +> (a -> b) -> a -> a -> Int -> a +> maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = > if (func medpoint) /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 I made the color slightly brighter diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs index 270eab67b..9bad8ecfd 100644 --- a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs @@ -42,7 +42,7 @@ This is similar to the preceding section. > y <- [0..height] > let > neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)] -> depthOf (u,v) = findMaxOrdFor (ymandel u v) 0 deep 7 +> depthOf (u,v) = maxZeroIndex (ymandel u v) 0 deep 7 > -- zs are 3D points with found depth > zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors > -- ts are 3D pixels + mandel value @@ -56,11 +56,20 @@ This is similar to the preceding section. > -- Draw two triangles > else [ps!!0,ps!!1,ps!!2,ps!!0,ps!!2,ps!!3] > -> findMaxOrdFor func minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = +> +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = > if (func medpoint) /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 > > colorFromValue n = diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs index 08fe85c54..7bba54b81 100644 --- a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs @@ -2,21 +2,26 @@ Some points: -1. OpenGL and GLUT are linked to the C library. +1. OpenGL and GLUT is done in C. In particular the `mainLoop` function is a direct link to the C library (FFI). - This function if so far from the pure spirit of functional languages. + This function is clearly far from the functional paradigm. Could we make this better? - We will have two choices, or create our own `mainLoop` function to make it more functional. - Or deal with the imperative nature of the GLUT `mainLoop` function. - As a goal of this article is to understand how to deal with existing library and particularly the one coming from imperative language we will continue to use the `mainLoop` function. -2. Or main problem come from user interaction. - If you ask the Internet, about how to deal with user interaction with a functional paradigm, the main answer is to use _functional reactive programming_ (FRP). - I read very few about FRP, and I might be completely wrong when I say that it is about creating a DSL where atoms are time functions. - While I'm writing these lines, I don't know if I'll do something looking close to that. - For now I'll simply try to resolve the first problem. + We will have two choices: -Then here is how I imagine things should go. -First, what the main loop should look like: + - create our own `mainLoop` function to make it more functional. + - deal with the imperative nature of the GLUT `mainLoop` function. + + As one of the goal of this article is to understand how to deal with existing libraries and particularly the one coming from imperative languages, we will continue to use the `mainLoop` function. +2. Our main problem come from user interaction. + If you ask "the Internet", + about how to deal with user interaction with a functional paradigm, + the main answer is to use _functional reactive programming_ (FRP). + I won't use FRP in this article. + Instead, I'll use a simpler while less effective way to deal with user interaction. + But The method I'll use will be as pure and functional as possible. + +Here is how I imagine things should go. +First, what the main loop should look like if we could make our own: functionalMainLoop = @@ -89,6 +94,14 @@ We simply have to provide the definition of some functions. > camPos = position w, > camDir = angle w, > camZoom = scale w } +> -- objects for world w +> -- is the list of one unique element +> -- The element is an YObject +> -- more precisely the XYFunc Function3D Box3D +> -- where the Function3D is the type +> -- Point -> Point -> Maybe (Point,Color) +> -- and its value here is ((shape w) res) +> -- and the Box3D value is defbox > objects w = [XYFunc ((shape w) res) defbox] > where > res = resolution $ box w @@ -110,8 +123,8 @@ These function are used to update the world state. > zdir :: Point3D > zdir = makePoint3D (0,0,1) -Note `(-*<)` is scalar product. -Also note we could add Point3D as numbers. +Note `(-*<)` is the scalar product (`α -*< (x,y,z) = (αx,αy,αz)`). +Also note we could add two Point3D. > rotate :: Point3D -> Scalar -> World -> World > rotate dir angleValue world = @@ -188,9 +201,9 @@ Because we consider partial functions > shapeFunc :: Scalar -> Function3D > shapeFunc res x y = > let -> z = findMaxOrdFor (ymandel x y) 0 1 20 +> z = maxZeroIndex (ymandel x y) 0 1 20 > in -> if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | +> if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | > val <- [res], xeps <- [-val,val], yeps<-[-val,val]] > then Nothing > else Just (z,colorFromValue ((ymandel x y z) * 64)) @@ -207,21 +220,29 @@ With the color function. The rest is similar to the preceding sections. -> findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => > (a -> b) -> a -> a -> Int -> a -> findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = -> if func medpoint /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> maxZeroIndex func minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = +> if (func medpoint) /= 0 +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 > > ymandel :: Point -> Point -> Point -> Point > ymandel x y z = fromIntegral (mandel x y z 64) / 64 -I won't put how the magic occurs directly here. -But all the magic occurs in the file `YGL.hs`. -This file is commented a lot. +I won't explain how the magic occurs here. +If you are interested, just read the file [`YGL.hs`](code/05_Mandelbulb/YGL.hs). +It is commented a lot. - [`YGL.hs`](code/05_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/05_Mandelbulb/Mandel.hs), the mandel function diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs index b1b21dde4..5fa20a0d2 100644 --- a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs @@ -1,6 +1,3 @@ --- The languages include needed because I wanted to use --- (Point,Point,Point) instead of --- data Point3D = Point3D (Point,Point,Point) deriving ... {- The module YGL will contains most boilerplate And display details. @@ -37,11 +34,20 @@ module YGL ( -- The main loop function to call , yMainLoop) where -import Numeric (readHex) +-- A bunch of imports +import Numeric (readHex) -- to read hexadecimal values + +-- Import of OpenGL and GLUT +-- but, I use my own Color type, therefore I hide the definition +-- of Color inside GLUT and OpenGL packages import Graphics.Rendering.OpenGL hiding (Color) import Graphics.UI.GLUT hiding (Color) import Data.IORef + +-- I use Map to deal with user interaction import qualified Data.Map as Map + +-- Some standard stuff import Control.Monad (when) import Data.Maybe (isNothing) @@ -49,6 +55,7 @@ import Data.Maybe (isNothing) - Just take the time to follow me. --} + -- | A 1D point type Point = GLfloat -- | A Scalar value diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs index d8d7c9793..1b05472c5 100644 --- a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs @@ -1,7 +1,8 @@ ## Optimization From the architecture stand point all is clear. -If you read the code, you'll see I didn't made everything perfect, for example, I didn't coded nicely the lights. +If you read the code of `YGL.hs`, you'll see I didn't made everything perfect. +For example, I didn't finished the code of the lights. But I believe it is a good first step and it will be easy to go further. The separation between rendering and world behavior is clear. Unfortunately the program of the preceding session is extremely slow. @@ -124,13 +125,14 @@ Our initial world state is slightly changed: > , anglePerSec = 5.0 > , position = makePoint3D (0,0,0) > , scale = 1.0 -> , box = Box3D { minPoint = makePoint3D (-2,-2,-2) -> , maxPoint = makePoint3D (2,2,2) -> , resolution = 0.03 } +> , box = Box3D { minPoint = makePoint3D (0-eps, 0-eps, 0-eps) +> , maxPoint = makePoint3D (0+eps, 0+eps, 0+eps) +> , resolution = 0.02 } > , told = 0 > -- We declare cache directly this time > , cache = objectFunctionFromWorld initialWorld > } +> where eps=2 We use the `YGL.getObject3DFromShapeFunction` function directly. This way instead of providing `XYFunc`, we provide directly a list of Atoms. @@ -183,9 +185,9 @@ All the rest is exactly the same. > shapeFunc :: Scalar -> Function3D > shapeFunc res x y = > let -> z = findMaxOrdFor (ymandel x y) 0 1 20 +> z = maxZeroIndex (ymandel x y) 0 1 20 > in -> if and [ findMaxOrdFor (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | +> if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 | > val <- [res], xeps <- [-val,val], yeps<-[-val,val]] > then Nothing > else Just (z,colorFromValue 0) @@ -198,13 +200,21 @@ All the rest is exactly the same. > in > makeColor (t n) (t (n+5)) (t (n+10)) > -> findMaxOrdFor :: (Fractional a,Num a,Num b,Eq b) => +> -- given f min max nbtest, +> -- considering +> -- - f is an increasing function +> -- - f(min)=0 +> -- - f(max)≠0 +> -- then maxZeroIndex f min max nbtest returns x such that +> -- f(x - ε)=0 and f(x + ε)≠0 +> -- where ε=(max-min)/2^(nbtest+1) +> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) => > (a -> b) -> a -> a -> Int -> a -> findMaxOrdFor _ minval maxval 0 = (minval+maxval)/2 -> findMaxOrdFor func minval maxval n = +> maxZeroIndex _ minval maxval 0 = (minval+maxval)/2 +> maxZeroIndex func minval maxval n = > if func medpoint /= 0 -> then findMaxOrdFor func minval medpoint (n-1) -> else findMaxOrdFor func medpoint maxval (n-1) +> then maxZeroIndex func minval medpoint (n-1) +> else maxZeroIndex func medpoint maxval (n-1) > where medpoint = (minval+maxval)/2 > > ymandel :: Point -> Point -> Point -> Point @@ -212,6 +222,8 @@ All the rest is exactly the same.
+And you can also consider small changes in other source files. + - [`YGL.hs`](code/06_Mandelbulb/YGL.hs), the 3D rendering framework - [`Mandel`](code/06_Mandelbulb/Mandel.hs), the mandel function - [`ExtComplex`](code/06_Mandelbulb/ExtComplex.hs), the extended complexes diff --git a/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs new file mode 100644 index 000000000..d222d3f9e --- /dev/null +++ b/output/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs @@ -0,0 +1,25 @@ + ## Conclusion + +As we can use imperative style in a functional language, +know you can use functional style in imperative languages. +This article exposed a way to organize some code in a functional way. +I'd like to stress the usage of Haskell made it very simple to achieve this. + +Once you are used to pure functional style, +it is hard not to see all advantages it offers. + +The code in the two last sections is completely pure and functional. +Furthermore I don't use `GLfloat`, `Color3` or any other OpenGL type. +If I want to use another library in the future, +I would be able to keep all the pure code and simply update the YGL module. + +The `YGL` module can be seen as a "wrapper" around 3D display and user interaction. +It is a clean separator between the imperative paradigm and functional paradigm. + +If you want to go further, it shouldn't be hard to add parallelism. +This should be easy mainly because most of the visible code is pure. +Such an optimization would have been harder by using directly the OpenGL library. + +You should also want to make a more precise object. Because, the Mandelbulb is +clearly not convex. But a precise rendering might be very long from +O(n².log(n)) to O(n³). diff --git a/output/Scratch/img/blog/Haskell-OpenGL-Mandelbrot/3DMandelbulbDetail.png b/output/Scratch/img/blog/Haskell-OpenGL-Mandelbrot/3DMandelbulbDetail.png new file mode 100644 index 0000000000000000000000000000000000000000..cf7fc3349c2c493a86f4130aa83d591cac510226 GIT binary patch literal 103505 zcmeEt^;48@xc37{cS(1Hgmg+M&C;EMba$7sprCX~gTT^Dw={xucMC|Dbf4$@&YVBt z{p~O?4D7P(UDy4oD^gum9vg!k0{{SQMTK{o0D!Ouel4LPg1>oBeQ6DTL-LSO)J8)? zTiQ@t2Y)2GY2UdI-dwzLE=4$c1FpE?he{1Bv?qgA9j zvv*aQeb=Uj*L3F1gVn=B75>kj2I%3zI*0Tuj__bj`Bi!IVfA=Ym#=`2W}!L=_3)_d zbp78$|2G@|w>SQO9|zmYD+He5`i+0`u}H#h_DX0Q_fM`N@YR~w=f2v?E4SCf9EklZ z!?PQ`B_+FVZB`vo^4&a@{D?xUPxdrOJwIQu{t5om3&leW{ecFwbB9ZDeL!?Iq3IM0 zzTYbI|BmIsxzUudfFbT?@rjyJga?B*i}y!rpc!KE^kU`UNAT5O+^?wgdstcf<#Hpv zuj1ahZ-9%>}34|sAuI3iVp?JV)NU-H@2cAj4rAPsXQMGhU_SmxbZLd}A4n`T7IB z_{Hw+?%ABVtj&nIKh$Pf+Mx2Gjz{P z;~pDMHA=yJZ{03aDAZeYBDv8-9hwa%3S9`tD0E?gw5?X1i+JqOwByibC<4D>glK#! z%&@Pw<+r~oO&&s4Cq9sihRO}SMC=F*VOtal8aE5scN@cnlS$leEo)vTOL|4z!v0&j zcgEk1(@cuzV{F7sW%LMJ?vy3lGnD`MmhXVkJ-&4Q5dSfgzV}Ed zy1&t`?l9Tn!zRMRWf3)ME<0kL>+|295eNS$EDxvkbYE+&)9jTiu{CeB&gfCB9 z5fEQPt+J7)30?R#BQW-B`%c1T{yeKAXrK6vOT_KV|K2}Q;6nn!`YeOkti-dV%ZVLgQJv(plCOY(feRaa+$e8r+5&%y6 z-|dcvq4=9vZHM!i6JZ%iIFS-iJRf8a$I8`q);XBHsun2wTG4TazmzO;iFl$r)d}AhwrJ_{VOV{xusF1>_7x+4eX(_x7z#%TmE+r`yl*|B3`8FLY;G!84sL#C z&7-b%6~^TYP2(mVS+-;&z&-7S%!X-L5)im%_ACy(pKG9o{&Ql!SXuHnjr1`8?tr#~ z#+wm8L?iKp`y5CRdf*TEw@wvI1FVwo(A;`ro*Jx9C{GZ^=Q48AH~`ZFPdx5LvEl*L}tmg*56Zc9E55W^QQ5kvI)Qb*+*lMt)UfdsK3Suh2|kBF!BQn zj?D987nGq+ACQvmb>;+s*f$Se0ONAZ0m9v|%=YZtd;sT=x^tnsapREmuT{6mr(xKp zJkCwZ5%sDhkp$t1gZqV4oUnqGfLMRP?-dxOi!6T3fkA_ESwd1fgjm#?9wArP1(8)} zw}TUK8bXfask3Nq)-QhEM=BdBDi+@TkmE;hDmuoKfp7)ZPGoeU(48?ZfQPUpZ9G4G z&VUJV=P+i795gf?tbO9*Si8-8rwrajl6 z4rcQA1Wr}0jW}|>2_B%mr6wC*AzEaJw~|ROE5Se)DiQ4dB9?~rcXO+y6~AxAts}fo z?@p5*YwX;hcEKGyBAD-YeAO(=X(XBqXZ-&DnWV{T@^=>Q`NPnRD&I$tIdR{d9WsV)%b0u>@Dv~< z-$oAJIs2fNEv&Z3L?n=i9>X)kn8nMAn)%nXp|~g)Hp;0>Ojq2a*k&h17)6E>6hz5zI;*-i)Me8LChi`vH`qGm&K!$<_Ed9GVgADCh*B z;vUM+Um8Vnn?=O3rHUuwta@3&hPy``gN2W+1*x`tVq-lK=3mcZOKCt!Ua9=36q98L zvn&2-UTiIcTGgb96gQS$Qh@N@xf*f(ScnpN-0QRlpng6?)zP_WuXC*I5Ng#d->&&P z9B447ZK6k#VqePsH`N}152pm*9~`+nNAe9TfBx?(#NSR+`CKnzX`|%|>8@b^B)3Ew zLB96=;XrN*<{f$xcw$D+yo{y=xeKTT+zklL#oMd2-;y?%rE$upabnflZPY7SpKHDn zz&*nDuWsOMJZd^uuBX~uX-|s6gOwnJLY-PaUt!A%q-1luNP52`3&2%B+28M8&M~)D zpGqxb?&|GO}5UhxLDcwsyDB+R<2mkm9DZRYD9N9*|$Ye5FHyX1_$u0QTs)arVP z)X_tS0Gv(q~pIoJD+3%1*Y% zxZIwU)C-AjE~y>J^68*kq|KVlSIeV(XKzPvR=GNWfL*&_-jT8JEzW*Qh902n72V)U zqPSov@vdVk@#dF#!?jF-#)nC0xa7{a_ab!cjqg55@P4*K$Qvw=3iC0`bNAc){^vg~ zG==@Yl)g@8Qb6uwb6N*4LOBNlyq79FoKU;5@TB7MSF+S#338Lu2)@}g0y&z{P^s>5 z)|_5139}z!In!SC+RlPYs%AdvEx4n@dH~jib?N1md){jSJzj`JhHBlSYW?g}eMqV4 zkt?Y+VV=eNinltqr#Zz~Uvzk=h)r%v*5B+?EjJ=7y<8St^!m5_ z=lA>U?ydG?B1J)~8`-jm~g24Ft>gp9Y$*ZB3v$wb*t+;2z z5d^Alg6)o7Ry$I&4cG)hgjmPCFqg%pW0d4-LCT)Sm<~fcl~_|FgCA(1tCby*M!;CR zFCVOj;6mj{LRbxpg5B=f&^(`Q6(HH(NQYQfBluFBcb<>vKs>blH8Fhc&hUW3`FTWUr33(xrJw(M11`WsuY`C&$}Hj~l5@~w7BGH^ zr1tcPia)CJ+XlkM6#aX^ieTE{bkG=GD&L$NK0P#}J2_2~;^5NmAr!h4bNr&}pbghF zu_yRqZ|LGfo89A<8Kh%4h*#xB|4K%Zr0B+)n@8Lm>29jT!!jH1l)*VuAv1aqo&hJ% z8$u7WbEfdEHsLz7l7q61D=9Pe?;Zzj{TdZ{2dKOKWf(%Cx7&twGz7=Svzgf(&+FH# z;*I)D8|@9#8Ai{2Uv95@+^%14e2{FW=zKowPc^MTx*1!W!p${ow7(P#T?=sZdcc;= zjd|NugR*{Z7TCVvJ~$)DWUEDm4eYZcr^E5?sOwu~btCba5#=gc3VYw1p=_MiK4oKt zCY2;kWer&Yza|rxm*y?%=UnyA-@q zO*IjFy&8nZjaM*b(D87SWe&%f`|C($>Tz(acaXmZKId-hmBgs^a5Tl?aw~~cs}sV_ z14&M7_LxoLt)r$^GBl3~tHMddX$WE6za3t}*tDS1eO_=;c;wH|Lbop{TabtnYTWlN z0U6(adE1?MqdcO?RqH%)W$9iic{V#M2yKp2@;r9TTGL?;I!BOvC_iFn1|1VWJv{m8 zar&##{JRS_SZy4D?ei7Cp{w0A-6cAoAKK38u$Xa}FhR!Cs%^zyyu{0xe8)ORAY7B- zsdV$Evdv2@)S?hQ*RU2SPa+5ITBgu;_^@y-5!9*a1j3k#Pupgzfa(30-7}+K6FE|j zH1xWQjR`mCS-Mx5+gA3XZA@JB`ALpSRfl@=#BkT;pE?GjDR_p5K5jbyC7-evV+>3> zoLEU#V!rTR(jdw!8QjBE9?LSx9Z9OnpWX<1%eV|bm}vEOIw+ae6|+5Ant4u{JCXK9 zu;q8MM;8;1ORTQ8zR+i-zvEVHIPgQuBKtS!WfB=vRmlRG`L1CV7~;arUY<&Uk>|Kn z@6@$MY99o~@*ZlLux}GET=XuIeyuJK8!{sbBCc+l0 z)|8`!Px4EYt3pIhSe@=aO_uz3$r4V2TJJvJd7y>2a;OBvR_e?B^xUA{g)n&f7W9hV zuD+fPtLipY8n5%!bXx2jpV^i{0-_zh5Rx3P`4%HY_#wX^gZddf^qN2XL5+D{V4+#K zncP*+Mm(^MIQhj9-X?ah@M+3!^W*1Hgww#15~bShI7<%gc^v*F)rwV9Ho;Zj9p^pe zxpU+=MV4V`3jd|bAgiun_Kw+4O&-ts8Sht1M|(=>blU7SM8LbizO<7eIj~B-Vw;%D z@|FlGoIVW_5&eE1DAJ*=LmWTGG^Mzb3QniLi>RG(HrJJRVG4$qj!rRa&U{C}`f{Pw z5c+hcV`kpR!|PR#&F2=$(~DGHd%REfA;8a*&#U9MbvPk69CQ2kHgF?h`MBHJ+sGc^ zRHhqS3Nb%S1RDIhf`&aR_g%d167qY}(u3QQf?&T=-<}v#fFQwi(0Vx8;_V%Q`6er0 z9>VY5Z*~kXgDQy+zdItKIb8?`t?U)bJcf^|4$j~ja-nS0Zr%Z&kcEzb&}_wB@&O+% zWfS`&g(d{73oCrBQ@Kr@p-syMQjqpE#(?aJ%Y^M+J8I}LJSi!md}m;9vRC_TJ^N#O z_QiGyeRxjF(JX~vs8~$Qf09FuylR$NrcwDM;s-OIaWp)7{WwNh=7TWDsMTYlP_V#* z2>Na=J2Thk#qBh~M5{vI!13<_!f2RDX@jj5ueq&qxF{8cdWSB#((YMU6;vnoB#V7v&3x+KF$THp2&b+CcA2_8^)lc1TE=Q?+4Dbbe5ob(w}fw}Gg9yn z3EGpgxs9BP#B$07UiDvknm8>vPfqjgy?c{tEbykFjw)8%Vd?AuTP*eUQp!@YG9qh= zk*17NNomy15bGhVaVf7huQY1YR|f5^o)&D?cCpMQJ~T85PevG?3Sl9KEL}eM_h=Hu zkEN$2RcqQagV(y^cXSi9cz!Q}CY^)YoI74gn17xjw(M1X=h&0TR=Aon^z9yo>zmrqBz5Qa{4}C3eD&%@Y_lS zXxNO8RnfiC%xgEcRqu$`)|8)bEYNgfsDpINF=2!{E=rMp>le!$Uq#~%R7s$2lcE-- zvTP>jUmPjG1~7*%Et(ZMiYM5xaJRr~63UjE0i8lFTm2AA*m`_y*z3h zn+0jntt}hQC==~%vUY7Hm>3F@9b(x6tq5|CXuCSfeGG>skYgQbC+Q4jP_s?YGXrD_ z!dCbATLaVkvogXC(f))fWljLd{|6!8glj^+Q;%b*K(&H*^R-}HFBVhYl7x0W4GX>6 zG5?<}- zZ$CjQ7IK2RtJnqZRbhRethPh&FfV*5LD+lJf|0B5O!umx04aCChA2bJ+T7<2MqMrN zJGUrnoR4s-&_->{iQ)+Hy}|KCpzKuA>TekCLR$sN2J!BoW8|M}v-XY)>~@2QTRB&v zPGh9w=gAV^fHOyC>HLofneA0y^ato~N%hJCSw`#Zoh~ zxg~E-Z;t~9-Hm_KBE-+~FvanDVKh3;)jH|{rKKQMLppd1XlfDXq1F7{%P;A=PF^6W z@JP6`nL-E9-%feS&gsaOS62m@CBy`@SF#UY1m3nu-n8tDjg}K-O#8cf=lx){Sh6KC z5Zbo?T4P0vZ^vJcm}630kkA)7<*`>_PFl*ny?6dK#O;cGJ6F;#5je#>>DW8SSON5DqlP0mInEhB!6#jEwwVG$2l?)39_pgT zY4VrR^~?G?G;2jk3Hy6Fl43|NBt9T~!`c3f>dt>b04hcc$!hE}tWo-Gg1S-B+L|GU zz1nps6t4x0o@R1_?)$jU+w-I7q4e*Jn-I|Om-&BgUkW(}l6YkO2M)J7o`&Lg-IlzI zcG$K5k?cG#@?boI;9q`m;+xma{bTQ)#v zQd8GERW5*6aG|*(v~%|I+Fv zY!@4;$&gN$)BJ#6?tCpcbdDLw>qSqOCKu}^JIvfF<*KPMZ_uRL%pXP7EI!P3dnFr1 zhjv|@x7+1~+g*0qVu!?s zl&{fG*%#nI@l-8_;8f_3$^9(iX7=_~$K)8U2PdnWPsXdR);9c}|)2w5(UgSIze9Ur!a!L|%U##zNPZ0kRGk3La%+wfB z>h4QD+tQikzCy_L@qRw4#vm12{my+C`G=vr)rAWb6Fmm*@P?wxzSU!XVmXk}Dm;-x z?ictxE@6;kR0+lAt@KjkY<49DC_#yXeZE~hZNi1TA9s4`xF>%Lk}D;y%)107F%m@h z0m*p^P(iky{=iv9`n{bM%qwDQ;jmq8m8RV&02eFD#amQgt(sCoFY z4r`ciU3sk=t;^*lADWu zhOreS6etlzCj@dsHA0B$CNc8Ryx1`Y8e_dDye}9aMUDm=59!#-A)bUaD)I)Az)8nv^GO6}8?x6;t0#_q|v0;SfJyF^f?%aA}dFy9t9=R(2 zZx%piObZ{|1j*7CVTu|A6Euy-1TuvZCJT%u?Awt6rg6@sROn>mo+Y#pmP?gg_HK5i zJ7HOT-oL!}?TlWkETDKfRR7XqMl`A2{m%s@0MTN$QR2fS?mzQy)kJH{=cr{`e4B3< zGG~ONN#2BIL?IU+?BXNp-ie?xnzZ^a0?A=?I`snAJee@GFj+LzEZ9S^abr;FKKA{_ zYWJz(_UsmTKC8S3INaM9{+N3Eyej?{5gqS>Nz1hc7;-ADp#W>CNx!At(|Gvj>haTv z2>|bJmnx$~2&66he(gV48;&$=mm7>t^bQ~#sRGI~f>!);-4GfQ5@$(Iuf?Oz5e1fL z!>K{55v6*ns}mHNv8;9kf34Fs?6mL$eZBicRq}=@_Blh(EZPm#qHlAnEp4lH#bb(| zYH=fsEipMwPN!kN+=f#(BeK2z70xg&m#e*U(2@N$i>I(?#99(&L_~a_qhx7G1wAO6wQQ;&;BQK4fdxp2=FUs>*h7tH zRY=qEel!+gcHS!2?j_-ShHAd>W5bp7PP@`AZ8#AY4L<(ouBKP2=@gJH?!WovM0xGn zM6CtBqpY98zyu#o0)|U5j{5oQ-pMGlT*3ouG#wf_g{t1XlE=4LgFpR}`enJ~?jp&H zv!15qu_V`;@)f)PY%yiiza5YM!W;VIi?*g7u`CQ*M}tU~KfPh|E;@ztZL{_ZBFJSR zjV2?3;Ts3djGlACA<%{JG)d+?^=m&(K3@wOi@uDg4=_^8-`?_oQGEMRN#3$A{1kI` z9CEgb`?%S2(kiY4@QBA{rE@x@|3I6tZw>Iis7Rt%F&Quxbvsi+=oDN1nPW}!yw~4B z{mAvlemPkgRA~duEv)Y6?QKp6_Wp1OuM3At-s+!nv2}DMM=tPIdAkU({$aMZaPWba zvI5p5Wp5wJ<}UGJ1l~Id0p2JgbQ;h%+o3zad$Lrhnst9 z)7R@8g^`R3BPcSw_MGt+pF1D;qrf=(Y{ecm&vPS(n9G5UE+jbrC+1)(IT9z$UEp`k z7aOAvwEsAa+AD@{{cNz{1C#sTae3Ri1jk@?j=}aqP$1pMeLftUS{gGp{=ptn^J}l& zypYl|R@}#%T8iDyk;u8+|5e$-S%CMINv83F;38xo)l&~2UC5#%ea&|lc?aRO5t3=y zvSyqs$AMt+G4v9j15ik&f^rUt&H3Vf}vcGH@-m;XqdBueA&z zeHBS@g|}dqtr6U+we&|pLW_C1s`SAqOE_LTH`@`E%VaAwu@w?7G60PkXXYhL2R-|v znT(Vq1!k|c#Iz)H5ob}m@#_yF)WCmCXTHJb@8smXdbW2l`;P_pmVdCF0l0CEzB41% z(WJIpu_RLNYE_Z7atoY1g1L~AVpPZoRDn|nBuoYh>_A3#7c}8RAR=?Tco21PyOI}3 zG6;ca{Ros6YIyp|wGmXs-CAWFFt>z*;CpixX?82`Y}!<~UO!k7QMBLmBjvijZnMc|mh|i>V_6%fWEW(Fl|dDjJkmuUn?u6q+{GX7_d9 zINY_X`9n;Ujpy{BRgNTxh4x>u@LF8Z4g(BNJ&nsDkVswsF=IoSd30K=i8sr^^JsF7`Q79LWhgN0aW4X_6Q~OQP|$$tuzE?36BWK?{iJI|vezN?1HOTNoWm z2_8L6B)RUsa@1$w#sJ{Z(SeSq27wRr-&H?v9&?VWQR1Vz=#NO|LoMuM|2yxB4L$zA3RnIdS-8Ss|yvui?*+f z3CAp~NU<@QjLFnfN&XNm$~m>&(#^XFc*OBwX;V&nRg;HNEd2|%d!_0;JB$@q^=>hU zyQ@S8w~BJnLNex1Jv47N{VMyY%`EuPUDU3eh@w8i%XMtM?;}?Dzq?T_{;dtLTD8E) zBld+&^OmOt1aeH)Dw*Gi6sy|{8eM_$fvMP&`3rph(iM0B+Lb_gAL1?u-9Mzqm-Bc^6ajv)#=E021Ktr_mfWB7@{W=2t zV4FA6y&j_5ga>LN?A_;973LK~29?PNLJ0j?Tt?g0q#k^Fid*&(VBZ-o1Bi=60nu+QqmR*0W^S+8+%n>ZcU* zD_K_N60@^8{xi;9gDphT%TYiU4$s&+9WH5pT2)CZRbY$cC`2KKI|#vCarP9-opvcqleACu3YECU46gzco@i)^I7^M&bM0B zFW{_m%TyjU{CQFd(Mvh0~U?U`}7}XdTD{F_SrGHERuy%KuP}o)%i#M-AHP8wMKV5Kk1BCaBl~QOPf5XmY%L|2-i7`p0 z^m`<}`i@F8OaFc$R}HuoM7_oWnuHB26FqH)W59JyEDRPRz{*JbtQLZni z*0xkZ5__#?0X;kIzJGV48O(yW67agOgQT!7V4E4TD`az!V1Vbf2|rf7Sk4{fn7+0C zeMi98^PM8IQ@wIldUIRw%@S>n~Q5$=^rgRq8CmsjtSLNdEU1fOlXzhBEZ&4IkAAZ2f) z_M&_#734gMi0^lwRxbT3ArdK4B`uhTrLB5{#dJGE4W>d91L`_BnpkRZJwai*gGHi4 zTbU)gm=iRlr?RQ$jEqaBSSCMCB(t$jMrK(XH?@9>zv_x?%C0Pp`cW=e=${wE(gI(M zp*U*Nw!_A?1m{d1#e;GA+`PMxdmDa64djRUL7isv_gOxrP!{xBnqL`CpW&Z=X@(JWrd6N@FdNaNqdhc-V5DpNWz%f` z&@fzWIT|z8_aEV0`tfehbC8y)&9{(eK9#O{pnwob!VbvhHW`Z*#mp9Oj8i4!UzgKK z-WeUX;$zks1pX_gewa=w=``UjF$Y?KCJy>hc+!6(L*XUC-#2#rCRJ=b9|oXYbY76G z?_9@AD{9uGTb_G^#)?`xr84d&9SiPYxJFY6RGsPL$F*fiTi9Pr4nX0k*j-C096<{; z#!!-4CC;XTaNSlJ8u=pH(7@4xYB3GnSgY0uxUT&XXws#jA3bbur-cu;xvYl#Tec{i zP2g?vEQuoB9TN9&=X$CaTP>x@QqEA+%1`@UXwR1+h`fH+byoyqkATCb`rPxXn~cv3 zDQWf)d0N#ML{_!nb$=pHJI>LBsFu@RmxJ2yteS5%+ZAR6Lg$uiutTYtiT=yj{l_6i zc)sc55^^sqTH5}Mb7fGWi`^&4e{dl?*!G?4A48mZ4a#5d7m6Nn(FQ&dDaTw1~H z^#0i#&K`Qiuu}U~M@K_kQa=Ez$L8bAOKl+cpe$mt&+s--Q1KrXtm!6-o^ozVyWXjJ z;53HL^jo`j@f7_&7(5dF8?g3Ma?z-n+!)K6gdK@u*~!GjtE@EIdvdt$6-(N9s|OnZ z-*Jel(X{v6Nw6f;2$=7BI(*Rd@`pdPd=LZw22jxwf@IZ%0f9H8GqNVLXBD7VXq0$; zh{Hk0#~hjy@@$mt>y6ks;2|xUUu>lClZ0Ror{csQMVanub4<14?_VdNCarfDjF{>F z7ppL4w*%#xX5^t?5j*$I-xS?X?!l9`;x;8*uTnC-xK|LbV0)1#^X0cWNt8O2R^qa- z04-$_BX-5(n&J5r89GTu{L}bLRSg0XgWu-Az^or=*@@CG8Yd_pm%4%*R3K~-mzl(qDp$ZT03<(`>T zT)BKm*3?21R&|B^$ZpxT7fYKSO4jB2kWyNa_w;S2q8Z0cXEX&`AE1n{cqP|exe5S6qGOuv}JYhHI)ok3hRzRAj_gUhJ9s~f`JW;q+ru# z@z2`%^lIi&s||j8I}i_I>Ydb2b~Z_$wOECy`t@cyi%Wz!$3TP$`ouaKK~S3lCohmk zk?SR&7~#gkDSjh{`iuYCgjkq2&rT&9vI*J(;&?R!5BsqSKnv#!r3eF z4s~ZaX-~B`iuaUH9_S|HOsp%_tzd*{`Cd%8ev_5qo?Vxq=l{j z7r_X1ujjPL|H=9=Y&C3b+j7^=cnQX+ia6?B^q$*I9s%Pyme1+TrjzT{{8$9X@l^&o zd8F_B;#>N{GpL1Cc~snWnh@d;mBs;bpatINaw7ndCS8&%NI(ZZ$A;iO3j5zz5@kP+ zjaZ*bd&VU>vOvE;DWpUd~G8Z=@VUf_?vodJ5J(#7ypst-P>&os!kA~j%BBH z-1bfUN+t)!&Lw>_o>0}wPcZu&UxPC9;KRuM^<-F~3{|z@a!J9eeZ}*$Af%Y=cubDW zZR))19oL`;o!fJJKQx-k*kT>an=8n$hQmcDn< z7oS$x5zWbPWIC9>cfE9e*MNfV77~&JKLA=>9z=l7b3paY%}$+>eX6fj!@IJig1NeJ zCHU8)$pSQzP^(rMG@)TAI%)@-rjjf^k=T8El4gUUYV#~L#pu|u9<7z$8v~d51JO@M zX!lLjP3Ayn^H7<&yXAi(g>rJri84By)#+nFqUeG=NoDzkcaaMgYGMN?OX*u^s9oWYK^$&rj^#UT+IA-;S5FC;jz`YTz<*Ghx|JeGTDV)w~hRw43wYWLX?Q}&PXkOX=B7~Uk_iL6vR9$@$ z=bo?&D>*xM5MAwZCi2OW>mbk)9P2LSWuV;Qthrr(?AefNt&|XmoPQhv)5&R`f7UwK z&QI1nlVu>*L!EBdUj4BvtS-i43hmk^6(a_00>B|DHsV{1Le#sOy)FAhJia_ceKvTC z*~!9!JhLz3dzR4`8qGAS0ce9xGC9H+19oJ5?6C!%;tAaXGsNmGW;` zTLAx(AZV(k<`0h^<(o|WdT-HkftkZGF3DqrAJbYXW=nQHzfrAOFU!0)dNwmUsuA=s zF2(f(_l=P^^C{Iv7f&=XHkWQW02wOE%?2YPFUGl6a!Fw_I6 z*oDe$CT*LX@+ZdfD0aN`bp{3oF#fs;*n-j0yWl||Z5`=~8U=H^PxsyKRiw%ezzolY z-2AE_Z=k0Mai@EpdHVFCI<05QE_p7RbO4N78nFd7A0vPHXyzA)2=3euKmrWjFX!{d z+k!k4&M}iNFrNeFnn8t$)d$o{clBJ|YECC?%qA6RVfp4q?ZJ75xAz^MQNiE7Au1)s z%uf9-jbThC?^%S)tJ!y1B84M5vV}P>0;^sp6KS`IGtB^=%uV*mUAx^2op|}xJF%R{ zDH??LyNgmT>FzQLhYA;|-ERkBgc@7azed3@?W9N8%+FIT#O8vM@0v0ipPCQjYhP`X zn<0n7*fa;VCtp+rTyp_CV`hMQIX_`bOOs~N6eDI~5eHa68Y#&+Q!>OFu~k&Lk-nRi zV$d0nrE1E^$goGzzu?!=&`4Kj-{=!_y_@d; zXLz|i>(Pdr(~2*0AzVq1iw(MaqL|a%ROh4j-0jYpAqapjxM{#4%GfzSWaV-dwc}P2 z71py(*>#X~qd%ljAIYlX{L8RglTB%QBBS^Bd63YdU&WW)_P$GTh|ZeVi3Suo>dT~p z%t^-LL%b`}7a#s)^MPRFCL!vO@va$@{O&XFu&2e6L5lujvt-R?ob}$?B2C4xmTL|8 z#*ZuFJ;C|j-=*Hogkh@6fTKBh2vu*TidR5_ardz|PwcE|`DkH5sI_XbHe>4&zT|@_ z9B8{nsFo$OOQC&mB<8$;`F=NrCis{p1XV+$bu;|j&k1N}Ve@t2cj`X_GY6JTs8@A( zN!TVO4F{0>^=xUn_sskWhYT){R6uOt9g?<@8fNKM1w}+UPu)x@4PZ`mayxllh&L{v zgNEOCLXdYNO|77B_O?rG82Z=$>DXkc2xg7baoLC1e&0v zW{0a}yLi*Ct@G%i+XhBGso{*^hTpSpgJnyf->?P5N5BIH%M>`In- za?+M{5n1R*AX>FcSFllHh_yHpOCC8<@Zn+P16p#R&rXIU!v=8&3 zOb+UjD;9iv{Gsx1AZa1eT=MDzcIAp5N5g$kx|1JpDoj6HWQ11joH^Mb^H|0K<=NRYUSDv(mq{)@^na5%vboJOJNcpT-`1 zaS+B|O>pgt@=WJU2zdFoPuecRO8l}5zS3LS2d}jWA^5K6)h6;W0Ug(*KBzyb%{%Tz zw~L%70M2x+cCbSwnIx)s{Pl8z;eFyKp>BLsp*3NdN;yB;X%R-=5>$V_xC9u1{O~B( zxKX;3JuB(OKU0DHFsz@&R1KVftOil%*L8WIsK8rneCxaK4M5KHuBn z!jLULp!vbtea*Dj`bB=B+Aw3?6K-v!WfrenhP)oxabLJAtk^WbH(;eQcMeQvc&nKj zu`HtzYA3){X|!Dy(@Pm4S)Q5xj?q|KwlCG#OSJx>hX(eQPcMPWYuD4H5^?$BoDk^M zxtSK!(so;QdQI?mUo?6Byr&YH2m@WBxS>}c)hm5mP6{cv6r78HfbpkK5K{7F135@m z0_@}b9d$)56G4}}4KEqcZ2YpbyZg@#JPRl|0(|e%c5EsBCD|EFs_YhQnl>pZo(Z%m z*-aN5D*1+2QH{Atw7k+#bH(V{{Gs;Pup72}^$F1)13thSOYV`=RtM$DW%gj8`qp)U z@Qw-rp~Qh5+b3o*>ttzV&2hmKRqFCC>0IcwiKw?PRwoi}4yw9C-JU(URUp#M_I>lv zffp)BOEbMHO6a}6RgQ{r6H!>Qy_?{O)R<0n4zVpt>K2wDa9-pkCy)gF+o-PM>*rA? ziTW1eqXiYxXzPq}6rla2Kf|&ao~qF4_DS^Ky9`W}lDzyhTW^yiS?{v8+MgqiAf-jD zqhYDiWS5bD{dBCAbD#D*-p52u<3;$ebeW`y?7XTs)djVg3^JpaaikY_UYIB4*m%9% z(4@djjj@i-kKzBaSNNs35L7ulQ(25cYrWA-GgF4;oZIe__G3@MtfuW#)=(SOQ2`^t z6%=9k+*)GrO?Bjxn767*s;Q6TXRBsh@xbhTe_f@rL>7aZN*l1u#x$K2|2tW|ehGy9 zJF7JF3iCy#E%DRiB18A#(dWp-GXs-4EC^VaHYEkfdD%*lSxf$S8HsUYg@EsdYo!;N zm!&kRoGW1*KLnZ>h`>({0g??}&lGmaW!%0&@B0FRF*^xEuDjOJ%J?r)fIv24SrDg8 zI%7cDHpBloFQTuI*>3pW+bS?Bkx;7`Pt`F>3|v&l~;B!RxKDlbh$}wd_&-EJe!CXFV=EmI()xv&RSF_c81M zt4$b6TVm6@z_m7c)y!-p9tn~<+=Ad`Au3W7o(}!XKt(5sV&LCj3VF3`qg>AG!(Ak> zt0Lect;F^-m}FBT8>-ALiAC%T2ss^nety~krYWw@TCeD*&%AV!G!jb!x!8ghmX>r_ zQxGVF?w3Ap>fd2DIalZo9S3lR?$A6Gr!lw^?m)p~Z9r;?eW9k^nwsy@ai;A93y zf=P=lm1@Jr)!T-*bx*hB`Kbs*gV-ioQ*8N8%_AvJ%$`HuCUavpBY~seP$nR%Bq4Y5 zYrkuPB{~;_OkCGAez{55>(vF-1+0{nvKQALfSICrS3)o#u}7Z-`6j@}7={W;?QibG zwJ0Rh2ay?&i>lmt1S`%Swv$61gBt(Hg83meA8M&Q{Wo`4jYyr7;-Q9YJlM>YDh$IU z-|Wv^#H%PeH`TK8oD^CI)M2%>tvLitn~M+>3E2gTdZXT#+W9yQfGzQ~7iiMt$1;4r zd*aS{>w8N&gpd%j;SfhA61qd$Wrkv}u>{nWD`{D~q5`8EBm=8{eVWer`25yT7@p*>- zwj%hiqq2yb3&HomBmf-Huy(Cg>R<(!P9^PfWS1H#gYe>184m-8h(}RPl38nkQbV@V2 zK8}dI-K+w3`x-?OfVIqdP?{nc@Xws8BaH{h6egzp{)4r>cRT`AJf-39hZiP3O$mtZePAtzE?+0sxIRqpR{2p8r4Sf4W20RNYCi;hkCXROX&pF_~4N{sUr zu2OsgUjV1104?$|`Z{jNQ9wXTf_9zEb9B@%H8W$-GC}3cfuneJ|3IRk9%e&?v#@c& zv6lfm#E}x(uQFaU9mpO^5WuqwW>1IhvygSNFh$S&;j`@pd`@Oj6u#^mA$Xd}PVV zA)`4KtqHklVCbSnqw9Sik`jzRp_k6XPiETo_hp&Mt>xr2Y5<>!0b%IQouv`N?jXzB zqe)}1R#A_qD7e%l>IvxG?B2bTPzg1;Xe7|?Hx%-Hh2vEN-a88uGKFx|fMZNl&a2Ob zrxW9zcW#eRzByFaLxFxEQvx(17y%+>9cabn4@VKnmjmn$OP|J-FQn;Ha7f$UxmCm2 z+LPiwZb(@PNQ*FqjD0`TfRmLAo%%=q-?`NknRwKZ3^|5^%c^U!^u;M631zPu!kFZHNIf499yh05fBNL zQ)+Z7XeF~$L=Dj>C`vEwZNtWg7^DI`f}&{ahXaWhl<19CtrlUfga^o^0}=K0*AunU zZzyxHw**@*L(?|NkWWbeho-ZPs;Yh3_@)u0OX=GAQ&`5VTy!-jD^{&M?K5)*>>^(Er{I0oG-bZ&V4|KJWKVN@(iN*N5hKKHt zreb9~Gr5)c`IBz+;lhG&5c2-|wGR*8{P0u9`!;N{{;*a692?@g%@%mAnvyE4ojZS( zfColp>mjTG8h;)1aHK3p%8=ZTI%RU!H;^jyLDjic9S_e99FSH{^|K&9l$Oht~bG17?*< zj>9%0i|k7^EAF)-47w!G)m#FXvs4Q=^7M^ zr#2$|^GRemW-?dL7%4<@>EglM@uARKX~PM2W^mq`Ywly(JEn@&fB1?va{7viT6Q7J zvV6ZpF^3O}49eXQ1g9N{yONg@e|)2IRmG^gC@9IxExUll#FH0(ND z9^lK|2L3LuCV(cUxx1Gph3U5*7MY6Yi(1Ss1p66^p@(d5RPxb2J@Ye(*DvKA9T~;B zWs2{p098d0hWO7*HR6V+eh5PLKy!5S2l%rD4zNNwDO235qnId6XYF;#OC!>Y+5$94K8<73{TKtZJj?(4O}N^nI@>K?F4x zKl&crK)^AK<=)eVoUSGLj{RcOyliNSEnXJ_GHDDVs;bx+<(|A1EFhyoo>R0|ha=up zv6o;|l6wbmrclg!E$iQXuzBop_zCZM<+DTsy~vYNc{nBTju^*|fSAc6WeXFhjV4 z8k9KByiS@bkGYA2$!Jdm`^4UHvtQfOlhIcbH8K~WiO&>wUWd8k*n7vd^3@&go$Fo; zHH4DbTDG<28NY){jG7Bdr>}K{Rr4ig23@{F)h$z(6C&JQLviyZhaAyIQjU;+c6jE) z>U@)VYI#jar%y5xSWH`iN4%xCx8cqRu_IG`oh;vv7yrBgUEVN6W3h3B!<`|S$vYdx zknH00d!_JFS&@?5?l96}i&|*zMx=dyQnKSe(MLXOi*Uio)d`Do3tpkz^K=ap!be3m z5Vj^t1SyAv#-yM&Og)mUy`Xzf0t6Z~TZIF3D$yPD^Da`tzf`47Oc{MK>#OwHCwFHjSe!INPg<_5!}g4^YXRau_2>o)slK|Z=s^S4rv#WJ{M1kz zq7kr%TK*sXWhQVoqP+LM_11Y^sR}&NF$rJkW=xFY;fUu=iD{kIehxYb$Bgv%~C{SVPX`Me4JeE9oMR zdob*u{=pz=|G;Fifv94;uv|+u*`nH!aG}nor%HF>Mk&$P_x-}%feWR|iBevmKKHj@ za$GUH1fk^~48x(r@f)d3AXU(0$DQx+_E+mS1tP6FA5ePJK#Q0g56$^!{nH<0eEQpXnfTEyRHH|gd5tuLO3)Zs|1sfw-e`#%ruRj#?21T$X59uolY;Y|mc*=4^< zom!30M*?ZGGY;gwMl=mA46*mQ>1^=@%upHI0x2g-EAJB+@nXJ>%3D$NvgJWl*@R(n zeMrtIJh8{h$jE9n)pQ~Zvp2V!ePsrBBqW7{<0IygL{pwWK6m4tRS#0a=x`%$CX(3V ziAk-`yc=3g%p&PZrCt?Ex&f(;a62K*F%5|tHsVZZ$Vv&`TA$< zhmSHNNt)Os_BNolhS@peM&j?G^1H<467f3clNQ_&Bh}KnEO`&QJj5K_evx8rTQ#iZ z|K(F+Z|60sa=Lw{KC|z|Gy!AHA5184oGhLIF6X%kX;Cr+Y8X*WwnChczwnnrd+cl- z-hWGFjC@JEUl;nGP1~e()KtOXBnmT7YW>XTBkSE`nyoew_D|y zv-=w0trY#gGZAzgCV!+JuoX|>{y4^*e0w~4d|_+sWoxb3!g{h~Yu;k?ho$WzyTs!| zSA?i24Jt}K-!5oGRKiKa;`)pfdwR!@xQ!9hlo5=MJ@{*Q`r0P|J0Q>BLIq}od~o;Z z%;PFTr(%#N2HsrH_8r;QE~@f(S5BYaF-?NmZMV%PW^0`$u@#jR)N5+55A_&fUgFgN zkYaj8-H%&U^>9RD&R_UBonXwA_-EUWDFL-w1%otsEANtrGCm8v>ay56tW}?bv^M!^ zJ63zciOH4gLP()h>*ea#f{cgyNymwD&;-xSw`9)q`T*gw8O8uzE%JD$cjJs}-e#fi=fWJC+KMEM>u0IrR&? z3J)tsBt}lzJ>58ufE3e|&RSE_Y>)gYj(zx?+qTo&gZ_1?z&GZ(DIfZ8Rww|x@wQ-^P4TMag0;oeN@1c&l#zDL~tA<1lgNI@Y1 z-8YeaMf&LoRhi`=6wf+D&F@URijO>vzk1BIVbDyiiiE8Fvy zrg8B&Uw6bS>)!)QK3T6Rca@dQ*xlg}N5|t|jhylmc+*3OD3|E)PnaBNebvUa<@AZA z-}s4!;B*FF?1-A6pUcE2*_#7CHR`~hr+~wK-3oTHT4mHHeEGy=n;171(ezQ*ZtG@z ze{EFn`e)pEH^Q(F-)8=S-_5=u#tgF9ciR!(WqVi%%%CiAu4`_O3nx5?A2@(reNLt8rtC%XpX;P}`oC3$NlSR2W!}MVN*) z2*tC!VRxu1X;5AT^A~S8JlW3k<9GxYKP8atqS7#=@@OMuu8h4!QWx&6pzN?=1(FCp z)aLkn==!Tf+%@ban;0xTL;TV4vv_%~{XYV3W`Ca&Pe?*S#MJ5AZ8M(R*)b?O;g6+M zyOU!Xm2ib9vE!>jWwvG#ef&0`&ifFnm&QQ220`0s`2AaDv^3J(SgG6{-fJ5q+NEnI zUg`eOVIO!qxC>n*Fh=RI54h^$1)7JWKtV6@Uo^l-7hHsAYh;|ZRAfj|&jbbSdjZ8z zt&Q1PyLcF~P(Qu496^$Q>IlE{e3)OLvrP>p3smMI+h)yAy`b8@V?Ygq{0Z?n}wE6b(h8 z=CI`!hYUi@})0;3-Y2*6a>V;lm9;JgLTV8au1rQ30lz>F^dmL~Lzem7&XCF!=t zZq>aJ-iKDN`>#-PE7$nRD0-KF9o(v2PB0{T=zVE@e60=%uTU9PTLt5a7D`dL!U+JY z1yAv-sOz&58se~t?`{(U?(N_q>@0=#AXU5Z;=BvPrdOx4k7o6C73Dq2`C4!U%y2Cj zYsr-?pUR1=h_h8pJ-5N%7hnd2XWX|`(iGAro^aQP%F&59OBbk+w>4>{eK;W|k} zEzEZb?@*3#pvTbDtzkTlLtClSO(@lj*YG3k!`JzrQ6BRasm@YLX7uNQbf0&|@HaltKA<~0=4IM5( z0^$jyi~ftA?SzWH=KYt%UJDLrgMYUaZN8xM1Wd{ggb|9&*xrQ(yeS=MrZ|`k|j2ok=!hp@47YODRQa}b((D# zR{Q&Eoa$#2z1xcAT4%I1E{7{MdEa7B25U?p8qb zPJqiwj~Or)RIDoLpV`t>lW6TC)APxOZbWnwsKjyI&qjNH&Ia^4z^+z-f2z5cpsN}t z`=NEuXRMdLODm!hDhEO$`6q<%WN^_I3u9>3ythZ2TM8^0tF97|ntD6}t*gh*0xI$Y1&G z-Vms^_|TR8Bm^xM@a^>a47i`4(c|cSu311O-z3G^y<6c$B|UQ%a&Kqps6LTgZ&9#o zVU|EpHZYbQSt_NB^vS19eh->oh(s;Ho~l>m=z-T?s+~eoq+njMs*F&};)(6Tr`nLu zj=!|ec)9PuRTH!Jf%nCnwN{}?4XL(HYl-|T87$Ke0u15RcB1pOisHMeM(S>Vk}jA3hS(rZRY+z99m{**u+9NE??ns9;kx)^NZr zM~`7w9~0EtPNq8JM0>nD*U^VNIW^9b`oh-&uYCg6(?!agtJ@#(E8c}AJ7^5y5R`}& zD;;siW<-WL^-0*@x!QO`U7K$eu~e8{xgO@AH{n6kocjN1gbHl&*51StYO~}Ii$YSH zZ?xOri9&JsO*I1L0GW!BBsqOuHd3n9P}!cl9#@RRdyl&=u1qH0aOGz6M(X3!TjcQ^ z6s3+AsWW(dAQLR>%2oRZ+F$>9@i!oN+2QRT>!v)mwtxlm1zaQ5I&V@0OS0RbO5u%_1GD-!v%ppfF9KcM6X>y0S&zMIEg0#*Q%6(SH4T z%fz!6%F(Q0SD?H(R80&_bk-EpE9K7GxQ<#QAF4s3p9pR4zh9CFzLmMzt= zdSi3pyTy%NaFpiE6;sQThP^~erbySDvBeeUrNd_}KswXejmmM(cbDIiStdVhHBc8D z3=1LFPn^hbui5-V+0)+o56d~WuPL9;4aqg39d@VUrm%`gp5TRQs;7hjr$(A&O-%-v zCYR4&dv)y(!4i#8(mhjsO2HXfVi`frL@`lJJONvCT168ZZl(@S7|}YCGAfeX)n$hD zM0I^yS`fr!Kt z)~%B_-Lv|6sAgB8Ryxzwjr%byg%nOhVjqq<;$VHF9R!T+a|`f zOauGtVLKlbV};6JL(}%jDn`CPbZ4Fm;3BN$&<{ZaDOhEgrsoJ>!<4iOsY+fFrGhPL z?Rd1e*%F2>vcU!m?SSsND$@Qg)keint`ExZM5dZxI%Un<3P2opM00cF=WQC|+Aq4D<@r7L2XmEK+C{v?TcWwYACvkc;}z;P zHz66gRa>OH@qWymnNE)Faz#7+NUNG&!}vM9#ggYhY|$+?R=TpQ5dpHA3^;85#pc(ho%o`oN~v@S#oc5M2p-|Yf{>oShL|nKjZgIy1dHW| z)_-Lf=0qpI{0&<%WM-DfQ9j!3D(6op4amr)*3J=$9vq?Vz|=y55aJUq-(SK9K>7FU6;B_8 zf^x%xLF*eQ_m3TYE&qA z@aL*ns$1F0Cfg3=Mg<88yl7Q0LfKh>2O|qxvwc4@Aryy#W)0O9yBUL-5jUi;r-BYg zFJcQ>G72`)LY=yc5bZIL+ah!)UKi#nMTu*A7{GAsDKg77HHqqg+zD$(5cubz!?hw1 zdr4dGiqvbtRi)pk#d;~LV<&W+m1){vp(RpI)!A^~Qvm#KV^KQiZ<4ih@T~0YMV3f3 zcK0SXVk_9v61^z9%+kafErwGLSTuHLXn0ZwCQW=qaJ9JHZUznl`bFKvuQHh?_h35g zIPFCReP^bM=$CzFg?ugPWnn65;ofIryFoeH!u}_*zfL>o&Nm^d0MTkXA!K%mR$cqN zWB~Q#BY-|_l8B~GKt!K8p|9y)aWNMqQ5Q9H#8E2RQS(fPKk1&9vmUVe0x*u`KBUj- z5P|!Sg1wFc@dW7_`FMR!%*f|AH^~f)(VQI}Kl;M zvf0fAHH_~7_@_jG+%SZamPKC!YTFUX7vVhFUe9X;=4o8E{3ic*9__!#7|?QJan?1% zGS(jZH`HIGBrV+1C*<-2VcGw)0M>QUE^IpG?)3HJzKwByHxVWA)L|Ur=;u=`d-~f# zp#TI1ETyu9fA}(m*YJ4Y-LLG_gD#9~w}H}_7|f^VWLF@M*6A@;f`4dZe5yk0w`jU4 zo7n1LAr#|CVKF=FJ0Aao2P2NWONJK}dd>2=p-fo8R`oMkA4NkzW(GFONI_MeJ2yn6 zr2i@kajV)4AeMjk$78j zX{FkyVB<%;Yg0k7^u<5?X?m1*G595xj0NT~DlS`HH6r}!d=09TFdl?P7wCInKy_Y{ zf-}l`ZYsc}-BRH%>Ox&U=4Py&ku!w8i&Fx8;mP}K6z}JF?U82tqI@V+Ek9~&f?zg@ z8Zr}}G(hE)Zv+WFN&@mPacI-JStra&er`sL_^C-?>dP9gA?Jtgeh1cy-mkh4}~K*_zfX9e-(A8qD~E}(9n+C?2&TuA*6$udt{ zW?@ix0V{FA$~=3+|41Z3eBmZ9r1(RQ_9gdq!kv%Tb{(Ssgr$FAhUDL$auPu1e@Vit z127m+NS|G~_L5Da`$T~|m(E0VypZ3lc`E*jhX>1s@O^Vf6JR(}1>P>Cg{eALL7fqT zmBW7m5Vi3}IN#q8;5wH(5iYpuN@8l;&bY4b@49cCN`Fnuc}Mb-{~;M|24%I1)I-XI zb<2$iAn0wc(~6K#8DD0<3h|K9)+(dU!%un<#)eyP^-M-0xzNoXTle${>xwQz)ElAd zd+=g#loz=0gWf+l$kx|@cX%Qi$nm)h0mIIE|6ia{8Y-9&IaH@zA*<;DJ>$tcgNYWJ zqiP!Q#6G2_a>o9fT>|&xNHOHo5xwy8WJ1}w zz^=4sUA*=L9yQ&Bju#P1_Bk>!<}8q!A4(`AkVZn|j2Bv>J(8n8$PD9d#Y(8q7eq9v zF9^&Cr{46GJUB2psvl-awTos!jSv0~B6uneHFVxG;v3~v%g*+*m&$%CG(3Zap5Kw9%S~jGeQErDNgG>%Te|*5o5Xiy{cXmQ zwf6z}Ac$;F#Idu}E(iX|p=;AXKYe2d5QZe~ACp;wE&`{gzi8QumEAab$-&mI$s& zPypta0H?ZN(k_){Gp&mQPY_8koW?sc5{j+0L z9{X|npTtPh%RXYcHcOUno{hI^!gR@Cr9gx5r2OmQMUmdm0!ZCy3VpeJ)|1{V7SjL5 zGGH7G(2V7@i(|m%^5KweQQx;NE>PLsBS{i-3eD#HndL1J3lAG<<8*`>#39fvL5z8L z7dWuYF|uaDtlswh4ufl>4EF#&M+QTjY=-DJ*C*N5`Tbx~q&sYEpZde_O0yy<9?bdY zbva~9z=@|)+2;{5f%^U$7fHOzGf#~1J*T4-UrQaAgpp3Gy9K2ge^_>Fx|Om+X|!xral4_LG;6DlbI_YX!h5s7 zaIignpi#dJ%0}M#qBG$A3v0hfeO5$`z#MzG zUhn6zCkO0*1l@eOStB7&PjA;~Z=q|RrtK0kvYCR51PJHgM5)kJnHFuQ*@7}}JQv~Av421JqOG9Vj?_Yis`i&*7Ftaj2QlUc2dONCjqL9`>$Xr6`Q!T_-g$8btIZ;Po`*C zU?x!@4XblE_#5EqRu|*Gqf4j9a)eR9i7>-kFf%)tm^t_WjMS7QF(abGE;dL{-OQR< z*2`+WqeW?+o6bv!60?KLQwsL|L#;BM=J54z@VkOlAl_jYa0bNBX z2Oaeks>9n!%UU9J><~4`uX7J8@+tTrMoAYzIH>wiVpKYoY|WOPNr!@E=9CO`=5p5} zaCEhkDUY+@^ z_I?2;$jzyE``rxA4-rJ!QSKF!VEt!NB?LwK5g7{6l1;I*^jjN(NEtMb&7=mu&*tB2 zn^t4gJDQTg0=y)qcV{Yy9gG}Wn6Sc=95`Ls5IZ>keP&VwpaBZF!A~z&YJPrT5&B9} zmsvj9Iz=;0&J`aqgD6wKrl!i172N=7$s4?pAjsI`f4M3SY`SrzA3b;>vZo&G-jzdk z?LS}vM?Tm}j58^eQ*Jtk$WwH@T*ZllEAc%{ zcqqT%Ro~McI812}i;-Iq^_3(xeL9bOIY-uiWJ|3%$MytN zY%YUbbndiPvlb+(Jn>^UC$4sSWfeX+&gklL0pxaPqW&mbg<&bRI6&;SRN{UM^Ulrt zd$x2Q#~{l(Frzf}X{^){swV6w5e?vo3okIKCfkf)hzGf5y8LIOX#0Y=X$%%Q2Yz~O4^B$1^ReA zyb_|OD@Ji*Pf7Aan{uchy^WfvpLWxfuk^k%)3Dh-JjB-IB~mi;cPH|awGOa@eh=?(Lj<{JeSlf$0eKD5kxd=yb}#>z=IsjY5`sNI zjLKj@TnnsS^amNtaeQX5uuip8j;H>-pFD<)(>a#NUlQn6&GVRsI_J0&YdDm6|Mx%~ za4Ds`-2J=bs-+N>tX5{5&CO9~%#E@kFL-6cm>2sgdGm|?7KFtsO;B_}Iv@7@NWW6= z?gHSV{ofH3u^^oZ{99dP6B1D+RunCnR`QX{?_CBNO#+r%&4lw3m)8xfiP7ly)KFl- zFIC=C8yfbM)VnIFUxoX@xtTee=QA4Tle)FbhV2LFuI=^g@AqqA0*-cT1VjhTZVk zmtmfJ?&UKo5i~F|K4ra+drwU?For{5+(Y0Z&(=-hjo{(M8xa0qmZO29NIDP(A*3?p}`Y60s(wqs7>fhsWpS0a4noAwg$)iiIRc)-BIDr`GDfKK3E#4`_?B%4lQvrA}0GXv4=9Mt!Zv zZ=FU8_iiQsPrr*#=$m1Yg8_Gn#QO($7c0?F5-M5DBy#;AUF2;U$??K^;1ku z2jp?aU+pF~8=-&tK7vXQI~-VH+VE8{M%7T7wHO0-LcGwwme^tmK{$DMG?NtZB$h4` zOxpA{-wHfxZ54=^kf2ezqXG?u^ zRlGh1JKw3 zInNn;LboGld635yKRAcYBRz`Nr9+^2&Xtvh?saNN>;r(oyt8jbEDw#9Ir|!yO0LPpX);D6zktiU59o`gwyrVM0-L4o@qI@_=k$g zvbm{#w~Z{5uZJ&xw${jLOSxRMije!@{XJx^XnNm@!ji7nd-Qdhve~8m39!Jf9|i1- zpeK;mjz+M@@iI_r>k1j%|LYKq2NbkSbb4wbt;*RM3?HWyL%Uww<^JFiP#!C*Mj(xP zsDOk5aNj?}zUB=8r|rHp7$2Jlp)K4;OVS7*TwPqG!LelaK&j>OD{SaDje__109d7a zEl_-oz!y(R`O9iC|c|(gz#0zX3&MoQQb(}qK`;BG@U5%LbR4GNU`Uk9W`(uTLGSoMe zeoLrYo+_W(Tr1egJZY+zpvfPa#oziN6-ArHtchtmP#~FUR4`|8FykB|z{lQjvR_zE zhY2WRcfM>LzYcXI+sv^ZCv?yL^O!)cWR#$w_boQ%!QyLy5Y^6o`mEZ*rv2eiUg;Gk z%eh63^ap);oDhjgw(Wn~B4@DBzVD&c_cO76YvvhJ?wNB_{>qbLT14g*>$QGkTj+O4 z3sERQY0H$zjI!zWshhZx^aYX#sg+qKAJ3@mX zBZ8o7-+^WEm371h2EenZ(?Avn*C`eKj!rx$0;YN>%epj69u=ptb zo8Im-g%;J*unHxv`?Uhr_6y2(Z60PHyTEM0SY4dQ?`l`TfKzB;d{zZ(*d!#`8U;$2 z-S=BgIsjggKr1G(^+wJZ)cGTy8#F7^0EewGHaVF%*jeU{s3ok)p!xfGJY%noV`BUz{HdoRYH8sEuD4b4{dUi?5vF&(jd5d*ml<^PEH zqUc+pVQ{HSU^)AO!kmnIbQE(_(Qt_~gB~m&#pz}MO85KDpAJNL(U1p!OmkcOj-uhz zVMy3%8fY`s)M=1Ss)xkue_M33}c2kIHpzL*9ev zhI2qLZLs|7%}n{&t!gBrYr$Wi99dOLWl_p`rz`^CK1A}+p-L0 zq|*mC3O2>rp_eJe(Pl{ji=KD((7674o!3|Xjhq^KZs{-*>;H6UtLMI@fFo)tEkV){3Bp668_Q67OF^W};rwnhY0bqj^2uc!=j{ zKZWs|z;-z*TDyP($d&YtPWMbatFvy@WvVq}NyRE>BdstLNMHy#(^dUm76fPU8HjIo zccH6CG;fcV0LJ>HXPI$geYRgp)|ccO) zsO8W;4XMdu`Xewn%c!@)3M(9ZWboFpYHhug<^D$Py$J`Bh*~!3vv>+a{S-v%m;w{p zr@Y>-xv-q5hh#Ho05d7r;DRldb_(r62)VQi|Ld{BY88hnF_9D$xc~pa#KWuu9krzA zZzi1!FOo-6$8bm;~)bIJ3h`{O77HKx3 zkM6k|j87jULh4w;ulNan2dQ@M_Hp}T$Q(FLV$ciEB54~*YE?{|SgkagLihK(fAs8G zp>*JPv_R}ASL7YE7jRELU2`9ht5PIlRmQGBg(c{LTCXa&<@LU`MLR*LMO3GJ_k|n- zPNB@3Q~tq-U8-;o`ikZPWp$Lr_C4)X1FP;NI%G}GGg}Y~i$4CK(SpOZr71Uj9F**R zuKlRA=TM|){+Z4E>VceSlM%OBF9_B0-20SB>K39sk6}Wu5F(~*oy5fnRr)=93S?B8 zU)_k5NWSSjv9Ss5^xA%y$K8^n=>d^|`TD8r#|V zg&p3Hq!K#JTCcvrFNuMWh=jYdELex@CqdLNi5?W+w{3mRtr@`3I0^~1(*U(C6o%?> zUbdfkYjD1mYp2|A60rRh%X%Ao~a+y^uVW( zGvJmffc5b-6YQ`IQygYfBg*#(BFQT%)ZbRoGJhddj^cnHgxhf@sPm$Kwo(umR>jT_ z1oATAzCWT`vWd4{k^a09$x15OZYjAB6*N4nc<&FB^b)pq-!Sq0`3(+SqAFSH=*U}C znrNr-^%mtnzBKIiHX~)`^rx6ciP+D6H`?L=F1r6BL%q}0(kDM7&zwIN7ZzC6#qdI5 z=~s?8pPhTQf4MjVSIpe&mqb1)~BHt&zPFiUrZ7yAe8bqTW0cUH z^7>nZyl(*?4k+51LH18#r=Wx!7CHuUT3FfQeI5cEiM%}Qg-(Y1^XUEe^z{DBE>k}4 zKH>hz#@aCZqTs&Hc)y~Wy#Exto#CV4*)9P`@yVlLjrx%q)FAg`?0*w*7)btM3jezn zq37jOEc9tMhja`63q|pQPgH-PkXr%V z*jlf|WMIxyxYpj8N$t>_R+t2@od>kuLj5w6t5^iTAklx^WT)TG_0#u%ZVRqC-ku30 z{A30v87Ti7S4DPZk$9}UZA@gwcR$0ucrdwUFQ=XHSIeDB%N~&(d?{Y^; z8Z7FR0w?tk>E`!S7(XX2td*YfF#b430T6mZS=?m&3u`ok^X z+m^Gur*k$m&~^Y`8bPU8#|Wvj8ETC_-?w+G{s;Y^3_pMGupr=m45t9j2V0byeX4(J zM=~CS;zSkOu10>X_xk<23Ki`@iMA_x(K8^!_OmPCEAR=4NYL5xq$|5as$TTDf<9Zi zTiQHz-Qn)vv4|qq9|q?T3yoajkz*se{2{PP9ZE2F@clz_1-{;~_cfA&w8rAEm=R5au4%2CRUsGTMTcja z?^njp+HF@S?$apvc9aLiKlhk&L>PZ${71bSq`QFXQ*<&t;PgPgcrMLYV|jXA-z|E| z;|N2#Mcs4yu#;G7QMfZ{nWjrSJrog&!yLL9=mp#q1{6Zghogl*QU8xx&}lqftFeE_ z>B`14KQr;n>tUQOeN-{Jd)DF_ZI^VSPmi&sX-zoiMRQ0{Xl|+ZQ=eJDW#W{&_N<9D zi;s1QMpC4ZK?GN%xJaz1;w?Lih zi&v6%tS?2z_j44poVlKvKGg}vOhn73RA_w{3D;m&7=;^o3cRLv(R%iEUWP89o}ShU z8GrxrykhSNz;ul9BTw>fj2Od98R>zM?5QhhWan9f*+@n%L!f>F@vi3O-D>9_QPuJDM@Mp8bno#m*pbB-Tn~=kqfu6WYyfpa zE>_Kw1}e!}6lrWY-k%c6alvyUB_Cso#a7CiwJ7taGs1cz$)oD5D)8{j8|V-%>SZtO z=~A>an|Q2gw#n=YGUP&~lhxGeMa)TwXZ-zJ?U})*Nj5TvA8}Z9Ij@cL68ZFDdOOL+m3iqFhXXp?$PA!nu9t+XtU+fr-vG zVEuZxAdL`6tO2Inx3^CBXq_-p0z4(ppTYg*Gf1VGz z*Ju7$he~(v?)j^|*pFm>lpommdpyCxWX+&ljqcYA&F9UyybKUK7A9Q;8hdm4u+z zQ3c(g3trx44Kj??^ZSTD6p}esum7I~5Z&EKk=4b^YbNm?aP;#12CBOP&xJx79;XZJ zY1vB6!`f^qCxeOK{(0|aE_BmushLc@(*FM>ot(jVha{_S2DlqDusogp5%D>D5e zHH>aW6n#!nk#+#6GlC4N7JZj&2pA1kldO$~%b}LYfg9$wLL91vnM5LN7LpZp!VEw`VW6m+22 za0Z6(#6PpJbACbE&$>TE`Jy^L`XhzT+mq7kp6&qsNkb z@Ok)KYRgw>igr!SELTb6+X)?ESb8&FiQxEyi|W7LI8@*HZv;VGeEtdFfNZZQgXRRZ zcNNR0#Z=OV0Z=|Qwq3?-+X2S`A9j0Dx>gAbjUjTuANduXg9dpql?FojiF4A&K#QOq z=yXtB3&<6z*RcghoQAV|?(}RqLLzs$Xwxw^9q?*ZSGr$?9AUU$Q-okaER}@l!#)C5 ziihJ_ER2EYQbzy3UX+|5(t5l7Mc9-vc()hf7g~th&vZzwcY(3q$Q%^9qki^peEkiq zJNWMw})f6ieaG?PU-iVfMzn81KqDFUFSd zo{tcka{qT)#UH@O@rx$wVt5U#KO`>JOBdB$IT3XebU&irQh^1B*phMn9>o@X#^mPw zn!fKACeXrArQ5%a&~}iqC&zScy4AZ(0aM}vg84y=5wp;zQX#EMhE)tO;<&XBq3*}b zI(Qt72Oz_^H;}V(P7N}v_|#h%7Gf)umH*Ya5QrZj$b=A(zC7oJCG*nmH%*ZF@lZa< zQmdGm`X#z_@FZ4J{BFxC#7Ei!V=MI?OQzoO(S?eCT7?BzCrCpN40|!*&LuLAlaroi zd<3PD+U>Uou8bdn`HB=e*<;Sajv$W`edlCDymwttf3eB&nf7_r0pf6Bg>_t^$Qh!bGa zV#GmyhT-n#q^u?IWW4-?&)ahD?R=>A>a}&E!zJXD+5A7NDr+aG9)V9blR94;$SG+L zKxKB;e19EoISSO8d4`Fo+)Et+b>AjEfl*Z|6*dz7Dm0lU5t1>Y2|Tsob5KerqF6PU zxuQg9+ZmJt3}r*@3H4L@j2RDPRr75sn314Wk7hB*N11Vd8{UA*16nvA5ZJ=kh0Fgq zCF-(`i5yG`+LeL*&_0$!&j=(naVPY5N(W;>u1iLd-_Ir?X=Ez5)Vl528i*dT@U+Szoxj$8I=abLrrl?mNI^(ywk)vV}N0wF!P%azYTAxkRyNNNG&w8CPxVP;e$tPYi~( zb%B8bdiBjUS?us-BVIdps@QO5Jk7oam6%R5rCRt7Hk~;(Pu}7J9TqdT!tUJ9Gm;Kd zfY%-vkT8aw(_n zR4)68Dnuy>?`UvkV523Udpl&}WXv(h{Fkk{@Yz@VK91yQWN8gx->n}K{ zJ@VaRa%~P!tF4CL`;v!d3hP}bSrQymlj(;|`dhZ`R@K%x)VhGHi8_!p<=iPc|A zv@|2b%N}-#CGA7Je8|t&{;C2=z$G--xU9opGzzGvpk@yA^y3c0MRsG)ThgP$P8fnF z+O%yNXIc9*sx1Bifccp=e90~FNezb)!|*~W6q?wh>3>|-U5EFCKSAjTVZ!em0ans( zsq)_)*AFOAWUx|-HyK;N)uj)k7gq}h`pbuX^)Mk>xWL7{^|~5+QUDlRwx$A1I)=Fh zc;USG{k|T!^S+Z%EEJm4M?lh5EFHze;EW4oMa6j$1xv}FPD6Fen*kwPT=(unzazK4 zC6J#Sq(~={tDR=cu(hl3-L!<={q*+T|Gl6C#w*BbH!8k;o^FEfx||8kFtu%ekzlgD z4f<@aOj_0ntAhZ@AJAE1A_e|!QU3WrcEKC#v3t3!L)e?#&WCGdC){ZR1Hcn>>Ncw% ztyu3__q#t`zz!7!pE;ygIM#-oQ`s^*QPWle|M=T&Ha&TG4n=;{{lPj5^PdCxNWMa! zGQp1=Mot(1@Y0}rZPnHRZ(`$Bfjp0{y+VEMNC7x%&CLJKZd7jQ+ggZ{lUaV*2zY1# zc8POVrZgOb(_fG`EbiNfD#Bh-1viAHNK~SG`X{IvE(#V*Vtw!9vws)a{PPJTIGO&Z zs>|u_#eWVu0~fTyz&&t^Wt`4ao;QI&g`-?-FG)e1cxyyPwD8aftY%sU`BLK1vWOHQ z#i${V%<9u6&Yb%hhQQY?%~j+clwk-jq}_Tn$s}JZlgPyyPkx>^2=m)gj!>x5w26lj z;P>WS=s-wv(>T62=MsQH!hIe0-#q2=bHHd1gl~_F5zd(>h5A#dg+-)}-N|-*LxzBCzFo3fBqGS(2iBm7N4WEIGAQ4{pNIHaW|Gvu4@poWcA2eVN@#+8*KeW0D-tED6@mK}Hm-<|% zrvuRms;z1mS@_6MD|UIvh`j0nYZJ2OoEo0lhTaR@P!MXuN6dPTG|S930O*}yez@5> zENK8-ELPYNZ8YQ6>m2PB!Jc#EL_goBK6=(^*h2f)39oHr$`lwcC&U}2aZuUb#Yq1b zYcW?1K3g=Q zxcRGx2wo&&JpE;-@MyFFy2`KDxy9Yj^tydhF9KBlnKTIY2fqOg+xjsgduk`(qbv-v zQ2H{}*hkO+v=s~laHz68y{rC^?qd&2i6&mktLDBE45^smYQ+&xF*G>KmbTc%@AWoe zp{C}pq3{G=u^WR!iPSSc-jAay=l4qm^C`Ad1_q!a_thkmbQkGHnRGM}gy!zbA?_5~ zr_+PID>QoqLIDEv4G8$Leow{RJgg@JGx{Ezt-K$$M053@Q{Mrn8PR_q;s<)$E84Fe zhW;vrsAvlq+LQ?`wdB94!wI?ZbQl^nq_;hGO;9IQWo2v042o7R<59rakzQ0rQDjKv z;~Yj!Do9f9ln|;2W15R6G;&NMQtr5>1H#f;!<7=#)6mID#6I#>oYFECy&4LmbB&&cx?t zzCNZWU8n5N)GIc=tzULs6aVx;>3vaZU#5NUbIN`nB4+G$6$4(ghl{`E#KilpuVmrg z@(2Pf4?}dPP)}+3Sg9Ebm&Hm}_$5BTtoHo7uP(ul_>Q$UJHwc#Wz;#iYrIfVs+;pF z+YYhb_JUI-cNVA|$u}$a-1_sc50dKX=~X?BzF57ef{t*q;r*R^KXN_JdP$ZT882ZJ zyW|VS0}B_fd7 z{_a)C-0x35bBS1WAQ)&e(TomOook9{!FW3Cxl5 zzeE}Ax_sQ`g|;8Featdumn%u|G_ny`LQXlBSL)}-=)N&1Uip9z`GIS7->9@cy~3#D znZi4W7YfgD4Et*Zs_PCZiKC`*#}moQiH4NQmQ|DD&o8qb%0DGGQaYk*FJ6k@Lo*g* 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 http://yannesposito.com/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/ - 2012-06-13 + 2012-06-14 http://yannesposito.com/Scratch/en/blog/02_ackgrep/ @@ -466,7 +466,7 @@ http://yannesposito.com/Scratch/fr/blog/Haskell-OpenGL-Mandelbrot/ - 2012-06-13 + 2012-06-14 http://yannesposito.com/Scratch/fr/blog/02_ackgrep/ @@ -758,7 +758,7 @@ http://yannesposito.com/Scratch/assets/css/main.css - 2012-06-13 + 2012-06-14 http://yannesposito.com/Scratch/assets/css/dynamic.css