-
Notifications
You must be signed in to change notification settings - Fork 0
/
Main.hs
124 lines (97 loc) · 4.52 KB
/
Main.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
module Main(main) where
import Text.Printf
import Graphics.Gloss
import Graphics.Gloss.Data.Vector
import Data.List
import Bezier
data Rule = Rule Char String deriving (Show)
newtype Axiom = Axiom String deriving (Show)
newtype Angle = Angle Float deriving (Show)
data LSystem = LSystem Angle Axiom [Rule] deriving (Show)
chosenSystem = terDragon
startIterations = 1
endIterations = 5
smoothness = 2
dragon = LSystem (Angle (pi/2)) (Axiom "f") [Rule 'f' "f-h", Rule 'h' "f+h"]
terDragon = LSystem (Angle (2*pi/3)) (Axiom "f") [Rule 'f' "f+f-f"]
main :: IO()
main = do
-- mapM_ (print) lists
-- where lists = [map (\a->(a,a)) [0..n] | n <- [1..10]]
-- mapM_ (print) (map length alignedPaths)
-- display (InWindow "fractal" (300, 300) (100, 100)) (white) (Pictures layers)
putStrLn (makeObj ( map (scale' 10) alignedPaths) ++ faces alignedPaths)
where layers = map line alignedPaths ++ map markPoints alignedPaths
alignedPaths = scaleAndRotateCurves chosenSystem paths'
paths' = [ selfComposeN (endIterations-n) (smooth 2) (makePath chosenSystem n) | n <- [startIterations..endIterations] ]
showFloat :: Float->String
showFloat = printf "%.10f"
makeObj :: [[Vector]] -> String
makeObj curves = (concat.concat) $ map
(\(order, curve)
-> (map (makeVectorString . addZ (10*2^order)) curve ))
(zip [0..] curves)
makeVectorString :: (Float,Float,Float)->String
makeVectorString (x,y,z) = printf "v %0.10f %0.10f %0.10f\n" x y z
addZ :: Float -> (Float, Float) -> (Float, Float,Float)
addZ z (x,y) = (x, y, z)
faces :: [[Vector]]->String
faces (c:curves) = concat [ printf "f %d %d %d\nf %d %d %d\n" a b c b d c | (a,b,c,d) <- squares]
where squares = [(p, p+1, p+width, p+width+1) | p <- topCorners]
topCorners = [ x+y | x <- [0..(width-2)], y <- layerStarts ]
layerStarts = [ 1+n*width | n <- [0..(height-2)]]
height = 1 + length curves
width = length c
(^*) f n = selfComposeN n f
selfComposeN :: Integer -> (a->a) -> a -> a
selfComposeN 0 function arg = function arg
selfComposeN n function arg = function (selfComposeN (n-1) function arg)
markPoints :: [Vector] -> Picture
markPoints points = Pictures (map drawCircle points)
where drawCircle (x,y) = translate x y (Circle 0.1)
scaleAndRotateCurves :: LSystem->[[Vector]]->[[Vector]]
scaleAndRotateCurves system curves = map (\(n, curve)->scaleAndRotate (scaleFactor**fromIntegral n) (angle*fromIntegral n) curve) (addIndicies (reverse curves))
where angle = -( ( \(Angle a) -> a ) . getTwistAngle) system -- -ve because we've reversed the order of the curves
scaleFactor = getScaleFactor system
addIndicies :: [a]->[(Integer, a)]
addIndicies = zip [1..]
scaleAndRotate :: Float->Float->[Vector]->[Vector]
scaleAndRotate scaleFactor angle = scale' scaleFactor . rotate' angle
scale' :: Float->[Vector]->[Vector]
scale' scaleFactor= map (mulSV scaleFactor)
rotate' :: Float->[Vector]->[Vector]
rotate' = map . rotateV
makePath :: LSystem->Integer->Path
makePath (LSystem (Angle theta) (Axiom a) rules) n = (0,0) : makePath' (iterate' n a rules) theta (0,0) 0
makePath' :: String->Float->Vector->Float->Path
makePath' [] theta curPos initialTheta = []
makePath' (c:cs) theta curPos initialTheta
| c == '+' = makePath' cs theta curPos (initialTheta + theta)
| c == '-' = makePath' cs theta curPos (initialTheta - theta)
| otherwise = newPos : makePath' cs theta newPos initialTheta
where newPos = curPos + (sin initialTheta, cos initialTheta)
iterate' :: Integer->String->[Rule]->String
iterate' 0 string rules = string
iterate' n string rules = iterate' (n-1) (replace string rules) rules
replace :: String->[Rule]->String
replace [c] rules = replace' c rules
replace (c:cs) rules = replace' c rules ++ replace cs rules
replace' :: Char->[Rule]->String
replace' c [Rule c' s]
| c == c' = s
| otherwise = [c]
replace' c (Rule c' s:rs)
| c == c' = s
| otherwise = replace' c rs
getScaleFactor :: LSystem->Float
getScaleFactor system = magV delta1/magV delta0
where delta0 = last path0
delta1 = last path1
path0 = makePath system 0
path1 = makePath system 1
getTwistAngle :: LSystem->Angle
getTwistAngle system = Angle (argV sum0 - argV sum1)
where sum0 = last path0 - head path0
sum1 = last path1 - head path1
path0 = makePath system 0
path1 = makePath system 1