From 886cbc4fc26e2cf167242a24c7576781983c98b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Gillet?= Date: Mon, 1 Nov 2021 22:08:16 +0900 Subject: [PATCH 1/2] resolve conflicts --- contents/barnsley/barnsley.md | 12 ++++--- contents/barnsley/code/haskell/Barnsley.hs | 37 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 contents/barnsley/code/haskell/Barnsley.hs diff --git a/contents/barnsley/barnsley.md b/contents/barnsley/barnsley.md index f696b9ffb..b9cb4f6b1 100644 --- a/contents/barnsley/barnsley.md +++ b/contents/barnsley/barnsley.md @@ -24,7 +24,7 @@ In this chapter, I hope to provide a slightly more satisfying answer by introduc | Hutchinson Operator | Attractor | | ------------------- | --------- | -| $$\begin{align} f_1(P) &= \begin{bmatrix} 0 &0 \\ 0 &0.16 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0 \end{bmatrix} \\ f_2(P) &= \begin{bmatrix} 0.85 &0.04 \\ -0.04 &0.85 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix} \\ f_3(P) &= \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &022 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix} \\ f_4(P) &= \begin{bmatrix} -0.15 &0.28 \\ 0.26 &0.24 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0.44 \end{bmatrix} \end{align}$$ | Barnsley Chaos Game | +| $$\begin{align} f_1(P) &= \begin{bmatrix} 0 &0 \\ 0 &0.16 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0 \end{bmatrix} \\ f_2(P) &= \begin{bmatrix} 0.85 &0.04 \\ -0.04 &0.85 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix} \\ f_3(P) &= \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &0.22 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix} \\ f_4(P) &= \begin{bmatrix} -0.15 &0.28 \\ 0.26 &0.24 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0.44 \end{bmatrix} \end{align}$$ | Barnsley Chaos Game | At first glance, this set of functions looks like an incomprehensible mess of magic numbers to create a specific result, and in a sense, that is precisely correct. That said, we will go through each function and explain how it works, while also providing a simple chaos game implementation in code. @@ -54,7 +54,7 @@ Now let's hop into disecting the Barnsley fern by seeing how each transform affe | -------- | --------- | | $$f_1(P) = \begin{bmatrix} 0 &0 \\ 0 &0.16 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0 \end{bmatrix}$$

This operation moves every point to a single line. |

| | $$f_2(P) = \begin{bmatrix} 0.85 &0.04 \\ -0.04 &0.85 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$

This operation moves every point up and to the right. |

| -| $$f_3(P) = \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &022 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$

This operation rotates every point to the left. |

| +| $$f_3(P) = \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &0.22 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$

This operation rotates every point to the left. |

| | $$f_4(P) = \begin{bmatrix} -0.15 &0.28 \\ 0.26 &0.24 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0.44 \end{bmatrix}$$

This operation flips every point and rotates to the right.|

| At this stage, it *might* be clear what is going on, but it's not exactly obvious. @@ -71,7 +71,7 @@ The easiest way to make sense of this is to show the operations on the Barnsley | -------- | --------- | | $$f_1(P) = \begin{bmatrix} 0 &0 \\ 0 &0.16 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0 \end{bmatrix}$$ |
| | $$f_2(P) = \begin{bmatrix} 0.85 &0.04 \\ -0.04 &0.85 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$ |
| -| $$f_3(P) = \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &022 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$ |
| +| $$f_3(P) = \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &0.22 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$ |
| | $$f_4(P) = \begin{bmatrix} -0.15 &0.28 \\ 0.26 &0.24 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0.44 \end{bmatrix}$$ |
| Here, the self-similar nature of the fern becomes apparent. @@ -86,7 +86,7 @@ To account for this, each function is also given a probability of being chosen: | -------- | ----------- | | $$f_1(P) = \begin{bmatrix} 0 &0 \\ 0 &0.16 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0 \end{bmatrix}$$ | 0.01 | | $$f_2(P) = \begin{bmatrix} 0.85 &0.04 \\ -0.04 &0.85 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$ | 0.85 | -| $$f_3(P) = \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &022 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$ | 0.07 | +| $$f_3(P) = \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &0.22 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$ | 0.07 | | $$f_4(P) = \begin{bmatrix} -0.15 &0.28 \\ 0.26 &0.24 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0.44 \end{bmatrix}$$ | 0.07 | ## Playing around a bit... @@ -98,7 +98,7 @@ Here are a few examples of ferns that can be generated by modifying constituent | -------- | --------- | | $$f_1(P) = \begin{bmatrix} \tau &0 \\ 0 &0.16 \end{bmatrix}P + \begin{bmatrix} 0 \\ 0 \end{bmatrix}$$

where $$-0.5 < \tau < 0.5 $$

Turning stems to leaves |

| | $$f_2(P) = \begin{bmatrix} 0.85 & \tau \\ -0.04 &0.85 \end{bmatrix}P + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix}$$

where $$ -0.01 < \tau < 0.09 $$

Changing fern tilt |

| -| $$f_3(P) = \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &022 \end{bmatrix}P + \begin{bmatrix} \tau \\ 1.6 \end{bmatrix}$$

where $$-0.5 < \tau < 0.5$$

Plucking left leaves |

| +| $$f_3(P) = \begin{bmatrix} 0.2 &-0.26 \\ 0.23 &0.22 \end{bmatrix}P + \begin{bmatrix} \tau \\ 1.6 \end{bmatrix}$$

where $$-0.5 < \tau < 0.5$$

Plucking left leaves |

| | $$f_4(P) = \begin{bmatrix} -0.15 &0.28 \\ 0.26 &0.24 \end{bmatrix}P + \begin{bmatrix} \tau \\ 0.44 \end{bmatrix}$$

where $$-0.5 < \tau < 0.5$$

Plucking right leaves |

| As an important note: the idea of modifying a resulting image by twiddling the knobs of an affine transform is the heart of many interesting methods, including fractal image compression where a low resolution version of an image is stored along with a reconstructing function set to generate high-quality images on-the-fly {{ "fractal-compression" | cite }}{{ "saupe1994review" | cite }}. @@ -135,6 +135,8 @@ The biggest differences between the two code implementations is that the Barnsle [import, lang:"java"](code/java/Barnsley.java) {% sample lang="coco" %} [import, lang:"coconut"](code/coconut/barnsley.coco) +{% sample lang="hs" %} +[import, lang:"haskell"](code/haskell/Barnsley.hs) {% endmethod %} ### Bibliography diff --git a/contents/barnsley/code/haskell/Barnsley.hs b/contents/barnsley/code/haskell/Barnsley.hs new file mode 100644 index 000000000..453723d93 --- /dev/null +++ b/contents/barnsley/code/haskell/Barnsley.hs @@ -0,0 +1,37 @@ +import Data.Array (Array, bounds, listArray, (!)) +import Data.List (intercalate) +import System.Random + +data Point = Point Double Double + +chaosGame :: RandomGen g => g -> Int -> [Double] -> Array Int (Point -> Point) -> [Point] +chaosGame g n probabilities hutchinson = take n points + where + (x, g') = random g + (y, g'') = random g' + + picks = randomRs (0, 1) g'' + cumulProbabilities = scanl1 (+) probabilities + to_choice x = (+ 1) $ length $ takeWhile (x >) cumulProbabilities + + points = Point x y : zipWith (hutchinson !) (map to_choice picks) points + +main :: IO () +main = do + g <- newStdGen + + let affine [xx, xy, yx, yy] [a, b] (Point x y) = + Point (a + xx * x + xy * y) (b + yx * x + yy * y) + barnsley = + listArray + (1, 4) + [ affine [0, 0, 0, 0.16] [0, 0], + affine [0.85, 0.04, -0.04, 0.85] [0, 1.6], + affine [0.2, -0.26, 0.23, 0.22] [0, 1.6], + affine [-0.15, 0.28, 0.26, 0.24] [0, 0.44] + ] + probabilities = [0.01, 0.85, 0.07, 0.07] + points = chaosGame g 100000 probabilities barnsley + showPoint (Point x y) = show x ++ "\t" ++ show y + + writeFile "out.dat" $ intercalate "\n" $ map showPoint points From 67f2d3dc005d4adba01ddb1a29ccc262bf35c9a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Gillet?= Date: Mon, 1 Nov 2021 22:01:47 +0900 Subject: [PATCH 2/2] updates according to review --- contents/barnsley/code/haskell/Barnsley.hs | 41 ++++++++++++---------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/contents/barnsley/code/haskell/Barnsley.hs b/contents/barnsley/code/haskell/Barnsley.hs index 453723d93..bf7024200 100644 --- a/contents/barnsley/code/haskell/Barnsley.hs +++ b/contents/barnsley/code/haskell/Barnsley.hs @@ -1,37 +1,40 @@ -import Data.Array (Array, bounds, listArray, (!)) +import Data.Array (Array, bounds, elems, listArray, (!)) import Data.List (intercalate) import System.Random data Point = Point Double Double -chaosGame :: RandomGen g => g -> Int -> [Double] -> Array Int (Point -> Point) -> [Point] -chaosGame g n probabilities hutchinson = take n points +chaosGame :: RandomGen g => g -> Int -> Array Int (Double, (Point -> Point)) -> [Point] +chaosGame g n hutchinson = take n points where (x, g') = random g (y, g'') = random g' - picks = randomRs (0, 1) g'' - cumulProbabilities = scanl1 (+) probabilities - to_choice x = (+ 1) $ length $ takeWhile (x >) cumulProbabilities + cumulProbabilities = scanl1 (+) $ map fst $ elems hutchinson + to_choice x = length $ takeWhile (x >) cumulProbabilities - points = Point x y : zipWith (hutchinson !) (map to_choice picks) points + picks = map to_choice $ randomRs (0, 1) g'' + step = fmap snd hutchinson + + points = Point x y : zipWith (step !) picks points + +affine :: (Double, Double, Double, Double) -> (Double, Double) -> Point -> Point +affine (xx, xy, yx, yy) (a, b) (Point x y) = Point (a + xx * x + xy * y) (b + yx * x + yy * y) + +showPoint :: Point -> String +showPoint (Point x y) = show x ++ "\t" ++ show y main :: IO () main = do g <- newStdGen - - let affine [xx, xy, yx, yy] [a, b] (Point x y) = - Point (a + xx * x + xy * y) (b + yx * x + yy * y) - barnsley = + let barnsley = listArray - (1, 4) - [ affine [0, 0, 0, 0.16] [0, 0], - affine [0.85, 0.04, -0.04, 0.85] [0, 1.6], - affine [0.2, -0.26, 0.23, 0.22] [0, 1.6], - affine [-0.15, 0.28, 0.26, 0.24] [0, 0.44] + (0, 3) + [ (0.01, affine (0, 0, 0, 0.16) (0, 0)), + (0.85, affine (0.85, 0.04, -0.04, 0.85) (0, 1.6)), + (0.07, affine (0.2, -0.26, 0.23, 0.22) (0, 1.6)), + (0.07, affine (-0.15, 0.28, 0.26, 0.24) (0, 0.44)) ] - probabilities = [0.01, 0.85, 0.07, 0.07] - points = chaosGame g 100000 probabilities barnsley - showPoint (Point x y) = show x ++ "\t" ++ show y + points = chaosGame g 100000 barnsley writeFile "out.dat" $ intercalate "\n" $ map showPoint points