dopairb 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +41 -9
- data/lib/dopairb/cli.rb +4 -6
- data/lib/dopairb/config.rb +6 -4
- data/lib/dopairb/demo.rb +9 -2
- data/lib/dopairb/director.rb +35 -5
- data/lib/dopairb/fx.rb +23 -0
- data/lib/dopairb/gallery/impressionism.rb +818 -0
- data/lib/dopairb/gallery/japan.rb +693 -0
- data/lib/dopairb/gallery/modern_ancient.rb +774 -0
- data/lib/dopairb/gallery/renaissance.rb +829 -0
- data/lib/dopairb/gallery/romantic.rb +703 -0
- data/lib/dopairb/gallery.rb +349 -0
- data/lib/dopairb/game.rb +84 -7
- data/lib/dopairb/input_fx.rb +63 -9
- data/lib/dopairb/irb_adapter.rb +8 -0
- data/lib/dopairb/profile.rb +106 -0
- data/lib/dopairb/reline_adapter.rb +1 -0
- data/lib/dopairb/scene.rb +5 -0
- data/lib/dopairb/scenes/bonus.rb +279 -0
- data/lib/dopairb/scenes/failure.rb +1 -0
- data/lib/dopairb/scenes/finale.rb +42 -2
- data/lib/dopairb/scenes/session.rb +21 -3
- data/lib/dopairb/scenes/success.rb +11 -4
- data/lib/dopairb/session.rb +50 -6
- data/lib/dopairb/sound.rb +57 -2
- data/lib/dopairb/term.rb +3 -1
- data/lib/dopairb/tour.rb +89 -0
- data/lib/dopairb/version.rb +1 -1
- data/lib/dopairb.rb +57 -1
- data/spec.md +3 -3
- metadata +10 -1
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dopairb
|
|
4
|
+
module Gallery
|
|
5
|
+
module Paint
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
# Friedrich, Wanderer above the Sea of Fog
|
|
9
|
+
def wanderer_above_the_sea_of_fog(u, v)
|
|
10
|
+
# the wanderer: dark coat, seen from behind, on the summit
|
|
11
|
+
return [150, 95, 60] if ellipse(u, v, 0.50, 0.335, 0.035, 0.028) < 1 # windswept reddish hair
|
|
12
|
+
return [40, 30, 30] if ellipse(u, v, 0.50, 0.365, 0.03, 0.02) < 1 # collar / neck
|
|
13
|
+
coat_w = 0.06 - (v - 0.40).abs * 0.15
|
|
14
|
+
return mix([35, 45, 40], [20, 25, 25], (u - 0.44) * 8) if v > 0.37 && v < 0.56 && (u - 0.50).abs < coat_w + 0.012
|
|
15
|
+
if v >= 0.54 && v < 0.69
|
|
16
|
+
leg_l = 0.475 - (v - 0.54) * 0.25
|
|
17
|
+
leg_r = 0.525 + (v - 0.54) * 0.30
|
|
18
|
+
return [30, 30, 30] if (u - leg_l).abs < 0.014 || (u - leg_r).abs < 0.014
|
|
19
|
+
end
|
|
20
|
+
return [60, 45, 35] if (u - (0.585 + (v - 0.48) * 0.1)).abs < 0.006 && v > 0.48 && v < 0.70 # walking stick
|
|
21
|
+
# the dark rocky crag
|
|
22
|
+
crag = 0.68 + (u - 0.50).abs * 0.55 + 0.03 * Math.sin(u * 37) + 0.02 * vnoise(u, v, 20)
|
|
23
|
+
crag = [crag, 0.70].max
|
|
24
|
+
return jitter(mix([45, 40, 38], [20, 18, 18], (v - crag) * 4), u, v, 12) if v > crag && u > 0.08 && u < 0.97
|
|
25
|
+
# rocks poking through the fog
|
|
26
|
+
return jitter([95, 95, 100], u, v, 10) if inside?(u, v, [[0.0, 0.62], [0.06, 0.52], [0.13, 0.55], [0.22, 0.66], [0.0, 0.70]])
|
|
27
|
+
return jitter([90, 92, 100], u, v, 10) if inside?(u, v, [[0.66, 0.60], [0.74, 0.50], [0.80, 0.53], [0.90, 0.62], [0.78, 0.66]])
|
|
28
|
+
return jitter([110, 112, 120], u, v, 8) if inside?(u, v, [[0.20, 0.52], [0.28, 0.47], [0.36, 0.53], [0.30, 0.56]])
|
|
29
|
+
# distant mountains on the horizon
|
|
30
|
+
mtn = 0.47 - 0.10 * smooth(0.55, 0.85, u) * (1 - smooth(0.9, 1.0, u) * 0.3) - 0.05 * Math.exp(-((u - 0.12) / 0.10)**2)
|
|
31
|
+
return mix([150, 155, 170], [175, 180, 190], (v - mtn) * 5) if v > mtn && v < 0.50
|
|
32
|
+
# the sea of fog
|
|
33
|
+
if v > 0.46
|
|
34
|
+
f = vnoise(u, v, 9) * 0.6 + vnoise(u + 3, v, 22) * 0.4
|
|
35
|
+
return mix([205, 208, 210], [150, 155, 165], f * 1.2 - 0.2 + (v - 0.46) * 0.4)
|
|
36
|
+
end
|
|
37
|
+
# pale sky with soft clouds
|
|
38
|
+
sky = ramp(v / 0.47, [150, 165, 185], [200, 205, 210], [235, 228, 210])
|
|
39
|
+
c = vnoise(u, v, 6)
|
|
40
|
+
c > 0.6 ? mix(sky, [235, 235, 232], (c - 0.6) * 1.5) : sky
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Friedrich, The Monk by the Sea
|
|
44
|
+
def monk_by_the_sea(u, v)
|
|
45
|
+
dune = 0.80 - (1 - u) * 0.06 + 0.012 * Math.sin(u * 9)
|
|
46
|
+
if v > dune
|
|
47
|
+
return [30, 30, 35] if (u - 0.33).abs < 0.008 && v < dune + 0.07 && v > dune - 0.0 # the monk
|
|
48
|
+
return jitter(mix([215, 205, 175], [175, 160, 130], (v - dune) * 5), u, v, 8)
|
|
49
|
+
end
|
|
50
|
+
return [30, 30, 35] if (u - 0.33).abs < 0.008 && v > dune - 0.07 && v < dune + 0.01 # monk above the dune line
|
|
51
|
+
return [30, 30, 35] if ellipse(u, v, 0.33, dune - 0.08, 0.009, 0.015) < 1
|
|
52
|
+
if v > 0.71
|
|
53
|
+
return mix([20, 35, 45], [35, 55, 70], Math.sin(u * 60 + v * 30) * 0.5 + 0.5) # dark sea
|
|
54
|
+
end
|
|
55
|
+
# vast sky: dark at the top, hazy light band above the horizon
|
|
56
|
+
haze = smooth(0.30, 0.70, v)
|
|
57
|
+
sky = ramp(haze, [55, 65, 75], [105, 120, 140], [170, 190, 205])
|
|
58
|
+
c = vnoise(u, v, 5)
|
|
59
|
+
sky = mix(sky, [210, 210, 205], (c - 0.55) * 1.5) if v > 0.45 && c > 0.55
|
|
60
|
+
jitter(sky, u, v, 6, 12)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Friedrich, The Sea of Ice
|
|
64
|
+
def sea_of_ice(u, v)
|
|
65
|
+
light = [225, 228, 225]
|
|
66
|
+
mid = [165, 175, 180]
|
|
67
|
+
shade = [105, 115, 125]
|
|
68
|
+
# foreground slabs
|
|
69
|
+
return jitter(mix([150, 140, 120], [110, 100, 85], (v - 0.85) * 6), u, v, 10) if v > 0.86 + 0.03 * Math.sin(u * 11)
|
|
70
|
+
return jitter([205, 200, 185], u, v, 8) if inside?(u, v, [[0.0, 0.80], [0.18, 0.66], [0.40, 0.76], [0.30, 0.90], [0.0, 0.92]])
|
|
71
|
+
return jitter([130, 130, 125], u, v, 8) if inside?(u, v, [[0.30, 0.90], [0.40, 0.76], [0.48, 0.80], [0.45, 0.92]])
|
|
72
|
+
return jitter([190, 190, 180], u, v, 8) if inside?(u, v, [[0.55, 0.92], [0.70, 0.70], [0.98, 0.80], [1.0, 0.95]])
|
|
73
|
+
# the crushed ship's stern, at the right of the pile
|
|
74
|
+
return [70, 50, 35] if inside?(u, v, [[0.72, 0.62], [0.78, 0.46], [0.83, 0.48], [0.80, 0.66]])
|
|
75
|
+
return [60, 45, 35] if (u - 0.80 + (v - 0.40) * 0.3).abs < 0.006 && v > 0.36 && v < 0.48 # broken mast
|
|
76
|
+
# the pyramid of ice slabs
|
|
77
|
+
if inside?(u, v, [[0.18, 0.80], [0.36, 0.45], [0.55, 0.10], [0.62, 0.18], [0.76, 0.48], [0.88, 0.74], [0.60, 0.82]])
|
|
78
|
+
# a sun-lit left face and a shaded right face, banded into slabs
|
|
79
|
+
side = u - (0.55 + (v - 0.10) * 0.15)
|
|
80
|
+
band = Math.sin((u * 0.6 + v) * 30)
|
|
81
|
+
base = side < 0 ? mix(light, mid, (v - 0.1) * 0.6) : mix(shade, mid, 0.2)
|
|
82
|
+
base = mix(base, side < 0 ? mid : [80, 90, 100], 0.6) if band > 0.75
|
|
83
|
+
return jitter(base, u, v, 8)
|
|
84
|
+
end
|
|
85
|
+
# low far ice field
|
|
86
|
+
return jitter(mix([175, 180, 180], [140, 150, 155], (v - 0.6) * 4), u, v, 8) if v > 0.62 + 0.02 * Math.sin(u * 23)
|
|
87
|
+
# cold sky
|
|
88
|
+
sky = ramp(v / 0.62, [120, 140, 160], [165, 180, 195], [210, 215, 210])
|
|
89
|
+
jitter(sky, u, v, 6, 8)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Turner, The Fighting Temeraire
|
|
93
|
+
def fighting_temeraire(u, v)
|
|
94
|
+
horizon = 0.68
|
|
95
|
+
sun_glow = Math.exp(-((u - 0.80) / 0.18)**2 - ((v - 0.64) / 0.20)**2)
|
|
96
|
+
# background: sunset sky and the water reflecting it
|
|
97
|
+
if v > horizon
|
|
98
|
+
refl = sun_glow * 1.6 + Math.exp(-((u - 0.80) / 0.05)**2) * 0.7
|
|
99
|
+
ripple = Math.sin(v * 120 + Math.sin(u * 20) * 2) > 0.3 ? 0.9 : 1.0
|
|
100
|
+
bg = Color.scale(mix([95, 125, 140], [240, 140, 60], refl), ripple)
|
|
101
|
+
elsif dist(u, v, 0.80, 0.64, 1.3) < 0.04
|
|
102
|
+
bg = [255, 225, 140]
|
|
103
|
+
else
|
|
104
|
+
bg = mix([110, 150, 185], [185, 200, 205], v)
|
|
105
|
+
bg = mix(bg, [235, 115, 40], smooth(0.40, 0.95, u) * smooth(0.25, 0.65, v))
|
|
106
|
+
bg = mix(bg, [250, 200, 100], sun_glow)
|
|
107
|
+
streak = Math.sin(v * 40 + Math.sin(u * 8) * 2)
|
|
108
|
+
bg = mix(bg, [200, 60, 40], 0.5) if streak > 0.7 && u > 0.55 && v > 0.3
|
|
109
|
+
bg = jitter(bg, u, v, 8, 10)
|
|
110
|
+
end
|
|
111
|
+
# the dark tugboat ahead of the old ship
|
|
112
|
+
return [30, 28, 30] if inside?(u, v, [[0.34, 0.655], [0.52, 0.65], [0.50, 0.71], [0.36, 0.71]])
|
|
113
|
+
return [25, 22, 22] if (u - 0.44).abs < 0.012 && v > 0.56 && v < 0.66
|
|
114
|
+
# its red-orange smoke plume streaming up and back
|
|
115
|
+
cx = 0.44 - (0.57 - v) * 0.55
|
|
116
|
+
w = 0.02 + (0.57 - v) * 0.15
|
|
117
|
+
if v < 0.57 && v > 0.22 && (u - cx).abs < w
|
|
118
|
+
return mix([170, 60, 35], bg, (0.57 - v) * 1.4 + (u - cx).abs / w * 0.3)
|
|
119
|
+
end
|
|
120
|
+
# the ghostly old ship, pale against the sky
|
|
121
|
+
ghost = [235, 228, 205]
|
|
122
|
+
return mix(ghost, bg, 0.15 + (v - 0.58) * 2) if inside?(u, v, [[0.06, 0.58], [0.40, 0.57], [0.37, 0.69], [0.10, 0.70]])
|
|
123
|
+
[0.13, 0.22, 0.31].each_with_index do |mx, i|
|
|
124
|
+
top = [0.14, 0.08, 0.16][i]
|
|
125
|
+
return mix(ghost, bg, 0.3) if (u - mx).abs < 0.007 && v > top && v < 0.58
|
|
126
|
+
[0.22, 0.34, 0.46].each do |yy|
|
|
127
|
+
next if yy < top + 0.04
|
|
128
|
+
return mix(ghost, bg, 0.4) if (v - yy).abs < 0.009 && (u - mx).abs < 0.03 + (yy - 0.22) * 0.12
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
bg
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Turner, Rain, Steam and Speed - The Great Western Railway
|
|
135
|
+
def rain_steam_and_speed(u, v)
|
|
136
|
+
haze = vnoise(u, v, 4) * 0.6 + vnoise(u + 5, v, 10) * 0.4
|
|
137
|
+
# background: golden haze, blue patch top left, river and fields below
|
|
138
|
+
if v > 0.58
|
|
139
|
+
base = u < 0.55 ? mix([185, 175, 140], [140, 125, 95], (v - 0.58) * 3) : mix([140, 110, 65], [95, 70, 45], (v - 0.58) * 3)
|
|
140
|
+
bg = jitter(mix(base, [215, 200, 150], haze * 0.4), u, v, 10)
|
|
141
|
+
else
|
|
142
|
+
bg = mix([230, 210, 145], [185, 170, 145], haze)
|
|
143
|
+
bg = mix(bg, [125, 155, 185], smooth(0.40, 0.0, u + v * 0.7) * 0.8)
|
|
144
|
+
bg = mix(bg, [250, 238, 195], Math.exp(-((u - 0.50) / 0.18)**2 - ((v - 0.30) / 0.18)**2) * 0.7)
|
|
145
|
+
bg = jitter(bg, u, v, 8, 12)
|
|
146
|
+
end
|
|
147
|
+
# the old road bridge with arches, far left
|
|
148
|
+
if u < 0.32 && v > 0.53 && v < 0.60
|
|
149
|
+
return mix([160, 145, 115], bg, 0.4) if Math.sin(u * 55) < 0.3 || v < 0.545
|
|
150
|
+
end
|
|
151
|
+
# the railway bridge from the lower right, receding toward the left
|
|
152
|
+
t = smooth(0.25, 1.0, u)
|
|
153
|
+
deck_top = 0.50 + t * 0.16
|
|
154
|
+
deck_bot = deck_top + 0.015 + t * 0.20
|
|
155
|
+
fade = 0.80 - t * 0.75
|
|
156
|
+
# the locomotive charging toward the viewer
|
|
157
|
+
if ellipse(u, v, 0.78, 0.58, 0.085, 0.09) < 1 && v < deck_top + 0.06
|
|
158
|
+
return [255, 190, 100] if dist(u, v, 0.78, 0.62, 1.3) < 0.018 # firebox glow
|
|
159
|
+
return mix([35, 28, 25], bg, 0.1)
|
|
160
|
+
end
|
|
161
|
+
return [30, 25, 22] if (u - 0.78).abs < 0.016 && v > 0.43 && v < 0.52 # chimney
|
|
162
|
+
return mix([200, 195, 185], bg, 0.6) if ellipse(u, v, 0.66, 0.41, 0.14, 0.05) < 1 # steam
|
|
163
|
+
if u > 0.25 && v > deck_top && v < deck_bot
|
|
164
|
+
return mix([90, 60, 35], bg, fade)
|
|
165
|
+
end
|
|
166
|
+
# piers under the bridge
|
|
167
|
+
if u > 0.45 && v > deck_bot && Math.sin((u - 0.45) * 22) > 0.8
|
|
168
|
+
return mix([110, 80, 50], bg, fade + 0.2)
|
|
169
|
+
end
|
|
170
|
+
bg
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Constable, The Hay Wain
|
|
174
|
+
def hay_wain(u, v)
|
|
175
|
+
# the hay wain and horses in the shallows
|
|
176
|
+
return [150, 50, 35] if ellipse(u, v, 0.50, 0.69, 0.025, 0.035) < 1 && ellipse(u, v, 0.50, 0.69, 0.012, 0.018) > 1 # red wheel
|
|
177
|
+
return [160, 115, 60] if inside?(u, v, [[0.40, 0.57], [0.60, 0.59], [0.59, 0.68], [0.42, 0.68]])
|
|
178
|
+
return [190, 175, 140] if ellipse(u, v, 0.52, 0.66, 0.02, 0.025) < 1 # driver
|
|
179
|
+
return [60, 45, 32] if inside?(u, v, [[0.29, 0.62], [0.41, 0.61], [0.41, 0.68], [0.30, 0.68]])
|
|
180
|
+
return [60, 45, 32] if ((u - 0.31).abs < 0.009 || (u - 0.39).abs < 0.009) && v > 0.67 && v < 0.73
|
|
181
|
+
return [55, 40, 30] if ellipse(u, v, 0.285, 0.605, 0.02, 0.03) < 1
|
|
182
|
+
# Willy Lott's cottage on the left
|
|
183
|
+
return [150, 65, 40] if inside?(u, v, [[0.0, 0.47], [0.07, 0.40], [0.18, 0.49], [0.0, 0.49]])
|
|
184
|
+
if u < 0.17 && v >= 0.49 && v < 0.66
|
|
185
|
+
return [70, 60, 50] if noise((u * 40).floor, (v * 30).floor) > 0.8
|
|
186
|
+
return [205, 190, 160]
|
|
187
|
+
end
|
|
188
|
+
# the great trees over the cottage and along the left half
|
|
189
|
+
tree_l = v > 0.06 + 0.35 * smooth(0.15, 0.55, u) + 0.04 * Math.sin(u * 30) && v < 0.64 && u < 0.58
|
|
190
|
+
tree_r = u > 0.87 + 0.04 * Math.sin(v * 13) && v > 0.30 && v < 0.62
|
|
191
|
+
if tree_l || tree_r
|
|
192
|
+
f = vnoise(u, v, 14)
|
|
193
|
+
return mix([35, 50, 28], [100, 115, 55], f * f * 1.4)
|
|
194
|
+
end
|
|
195
|
+
# far trees and the sunlit meadow on the right
|
|
196
|
+
return mix([60, 85, 50], [90, 110, 60], vnoise(u, v, 20)) if v > 0.51 - 0.025 * Math.sin(u * 20).abs && v < 0.56
|
|
197
|
+
return jitter(mix([185, 180, 95], [125, 145, 60], (v - 0.56) * 10), u, v, 10) if v >= 0.56 && v < 0.63 && u > 0.36
|
|
198
|
+
# the river, reflecting the trees and sky
|
|
199
|
+
if v >= 0.62 && v < 0.88 - 0.10 * u
|
|
200
|
+
ripple = u > 0.45 && Math.sin(v * 120 + Math.sin(u * 15) * 3) > 0.75
|
|
201
|
+
base = u < 0.5 ? mix([85, 85, 60], [60, 60, 45], (v - 0.62) * 4) : mix([150, 160, 160], [95, 100, 85], (v - 0.62) * 4)
|
|
202
|
+
return ripple ? mix(base, [210, 210, 200], 0.3) : base
|
|
203
|
+
end
|
|
204
|
+
# banks
|
|
205
|
+
return jitter(u < 0.45 ? [110, 95, 55] : [85, 110, 50], u, v, 14) if v >= 0.62
|
|
206
|
+
# summer sky with big cumulus clouds
|
|
207
|
+
sky = mix([75, 115, 180], [150, 175, 205], v * 1.6)
|
|
208
|
+
c = vnoise(u, v, 4) * 0.7 + vnoise(u, v, 11) * 0.3
|
|
209
|
+
return mix([250, 245, 235], [150, 150, 150], (c - 0.5) * 2.5 + v * 0.5) if c > 0.48
|
|
210
|
+
sky
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Delacroix, Liberty Leading the People
|
|
214
|
+
def liberty_leading_the_people(u, v)
|
|
215
|
+
# the tricolor flag on its pole, raised high
|
|
216
|
+
wave = 0.015 * Math.sin(v * 25 + u * 10)
|
|
217
|
+
if v > 0.04 + wave && v < 0.25 + wave && u > 0.47 && u < 0.66
|
|
218
|
+
return [35, 55, 150] if u < 0.53
|
|
219
|
+
return [240, 235, 220] if u < 0.59
|
|
220
|
+
return [205, 35, 35]
|
|
221
|
+
end
|
|
222
|
+
return [70, 50, 35] if (u - 0.47 + (v - 0.04) * 0.08).abs < 0.007 && v > 0.03 && v < 0.30
|
|
223
|
+
# Liberty: raised arm, head, golden dress
|
|
224
|
+
return [230, 195, 155] if (u - 0.462 + (v - 0.22) * 0.25).abs < 0.014 && v > 0.21 && v < 0.33 # arm
|
|
225
|
+
return [40, 30, 25] if ellipse(u, v, 0.425, 0.315, 0.03, 0.035) < 1 && u < 0.42 # hair
|
|
226
|
+
return [230, 195, 155] if ellipse(u, v, 0.43, 0.32, 0.026, 0.035) < 1
|
|
227
|
+
return [40, 30, 25] if (v - 0.42 - (0.40 - u) * 1.0).abs < 0.01 && u > 0.31 && u < 0.41 # musket
|
|
228
|
+
if inside?(u, v, [[0.40, 0.35], [0.47, 0.35], [0.52, 0.55], [0.58, 0.80], [0.36, 0.80], [0.39, 0.55]])
|
|
229
|
+
return jitter(mix([240, 220, 160], [175, 150, 90], (v - 0.35) * 1.5 + (u - 0.44) * 2.5), u, v, 10)
|
|
230
|
+
end
|
|
231
|
+
# the man in a top hat at left, the boy with pistols at right
|
|
232
|
+
return [25, 22, 22] if inside?(u, v, [[0.25, 0.32], [0.31, 0.32], [0.31, 0.40], [0.25, 0.40]])
|
|
233
|
+
return [24, 22, 22] if inside?(u, v, [[0.23, 0.40], [0.33, 0.40], [0.33, 0.415], [0.23, 0.415]])
|
|
234
|
+
return [215, 180, 140] if ellipse(u, v, 0.28, 0.44, 0.022, 0.03) < 1
|
|
235
|
+
return [35, 30, 30] if inside?(u, v, [[0.22, 0.47], [0.34, 0.47], [0.37, 0.80], [0.18, 0.80]])
|
|
236
|
+
return [60, 45, 40] if ellipse(u, v, 0.62, 0.38, 0.022, 0.022) < 1 # beret
|
|
237
|
+
return [215, 180, 140] if ellipse(u, v, 0.62, 0.42, 0.02, 0.03) < 1
|
|
238
|
+
return [45, 50, 80] if inside?(u, v, [[0.58, 0.45], [0.66, 0.45], [0.69, 0.78], [0.57, 0.78]])
|
|
239
|
+
return [40, 35, 30] if (v - 0.36 - (u - 0.66) * 1.2).abs < 0.012 && u > 0.65 && u < 0.71 # raised pistol arm
|
|
240
|
+
# the crowd rising toward her in a pyramid
|
|
241
|
+
if inside?(u, v, [[0.0, 0.70], [0.12, 0.55], [0.30, 0.48], [0.45, 0.42], [0.62, 0.50], [0.85, 0.55], [1.0, 0.68], [1.0, 0.85], [0.0, 0.85]])
|
|
242
|
+
return jitter(mix([75, 65, 55], [40, 35, 30], vnoise(u, v, 14)), u, v, 10)
|
|
243
|
+
end
|
|
244
|
+
# the barricade and fallen figures at the bottom
|
|
245
|
+
if v > 0.76 - 0.06 * Math.sin(u * 3.2)
|
|
246
|
+
return jitter([175, 165, 145], u, v, 12) if ellipse(u, v, 0.24, 0.90, 0.12, 0.035) < 1
|
|
247
|
+
return jitter([160, 150, 135], u, v, 12) if ellipse(u, v, 0.76, 0.89, 0.09, 0.03) < 1
|
|
248
|
+
return jitter(mix([95, 75, 50], [50, 40, 30], vnoise(u, v, 10)), u, v, 12)
|
|
249
|
+
end
|
|
250
|
+
# Notre-Dame's towers in the haze
|
|
251
|
+
return [150, 135, 110] if v > 0.38 && (u > 0.80 && u < 0.84 || u > 0.88 && u < 0.92)
|
|
252
|
+
# smoke-filled sky, brightest behind Liberty
|
|
253
|
+
glow = Math.exp(-((u - 0.47) / 0.22)**2 - ((v - 0.35) / 0.35)**2)
|
|
254
|
+
smoke = vnoise(u, v, 5)
|
|
255
|
+
sky = mix([105, 90, 65], [215, 200, 150], smoke * 0.6 + glow * 0.6)
|
|
256
|
+
jitter(sky, u, v, 10, 14)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Géricault, The Raft of the Medusa
|
|
260
|
+
def raft_of_the_medusa(u, v)
|
|
261
|
+
# the man at the apex waving a cloth
|
|
262
|
+
return [215, 190, 160] if ellipse(u, v, 0.82, 0.20, 0.035, 0.03) < 1 && ((u * 30 + v * 20).floor.even?)
|
|
263
|
+
return [210, 150, 110] if ellipse(u, v, 0.80, 0.27, 0.02, 0.03) < 1
|
|
264
|
+
return [190, 140, 100] if (u - 0.80 - (0.28 - v) * 0.3).abs < 0.008 && v > 0.22 && v < 0.30
|
|
265
|
+
# the pyramid of figures rising to the right
|
|
266
|
+
pyramid = [[0.30, 0.84], [0.42, 0.66], [0.55, 0.55], [0.66, 0.44], [0.76, 0.32], [0.82, 0.30], [0.86, 0.84]]
|
|
267
|
+
if inside?(u, v, pyramid)
|
|
268
|
+
f = vnoise(u, v, 18)
|
|
269
|
+
return f > 0.55 ? mix([200, 150, 100], [150, 100, 65], v) : mix([90, 60, 40], [50, 35, 25], v)
|
|
270
|
+
end
|
|
271
|
+
# the sail and mast at left
|
|
272
|
+
return [50, 40, 30] if (u - 0.30).abs < 0.007 && v > 0.12 && v < 0.84
|
|
273
|
+
return [55, 45, 35] if (v - 0.12 - (u - 0.30) * 1.4).abs < 0.005 && u > 0.30 && u < 0.55 # rope
|
|
274
|
+
return [55, 45, 35] if (v - 0.12 - (0.30 - u) * 2.2).abs < 0.005 && u < 0.30 && u > 0.12 # rope
|
|
275
|
+
if inside?(u, v, [[0.31, 0.16], [0.44, 0.24], [0.42, 0.62], [0.31, 0.64]])
|
|
276
|
+
return jitter(mix([175, 150, 110], [120, 100, 75], (u - 0.31) * 6), u, v, 10)
|
|
277
|
+
end
|
|
278
|
+
# the tilting raft
|
|
279
|
+
raft_v = 0.86 - (u - 0.2) * 0.08
|
|
280
|
+
if v > raft_v && v < raft_v + 0.06 && u > 0.12 && u < 0.92
|
|
281
|
+
return [70, 50, 30]
|
|
282
|
+
end
|
|
283
|
+
# stormy sea
|
|
284
|
+
sea = 0.62 - 0.12 * Math.exp(-((u - 0.10) / 0.12)**2)
|
|
285
|
+
if v > sea
|
|
286
|
+
wave = Math.sin(u * 25 + v * 40 + Math.sin(u * 6) * 3)
|
|
287
|
+
return mix([22, 30, 28], [65, 75, 60], wave * 0.3 + 0.4 - (v - sea))
|
|
288
|
+
end
|
|
289
|
+
# dark sky with a light patch at the horizon on the right
|
|
290
|
+
light = Math.exp(-((u - 0.85) / 0.18)**2 - ((v - 0.55) / 0.15)**2)
|
|
291
|
+
sky = mix([40, 40, 35], [85, 80, 60], vnoise(u, v, 5))
|
|
292
|
+
sky = mix(sky, [235, 210, 140], light)
|
|
293
|
+
jitter(sky, u, v, 8, 12)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Goya, The Third of May 1808
|
|
297
|
+
def third_of_may(u, v)
|
|
298
|
+
lamp = Math.exp(-((u - 0.45) / 0.25)**2 - ((v - 0.75) / 0.30)**2)
|
|
299
|
+
# the man with raised arms, white shirt and yellow trousers
|
|
300
|
+
return [240, 235, 220] if ellipse(u, v, 0.34, 0.50, 0.045, 0.08) < 1
|
|
301
|
+
return [240, 235, 220] if (u - 0.30 + (0.45 - v) * 0.35).abs < 0.012 && v > 0.32 && v < 0.46 # left arm
|
|
302
|
+
return [240, 235, 220] if (u - 0.38 - (0.45 - v) * 0.35).abs < 0.012 && v > 0.32 && v < 0.46 # right arm
|
|
303
|
+
return [80, 60, 40] if ellipse(u, v, 0.34, 0.415, 0.022, 0.03) < 1 # head
|
|
304
|
+
return [225, 190, 60] if ellipse(u, v, 0.34, 0.63, 0.05, 0.07) < 1 # trousers, kneeling
|
|
305
|
+
# the lantern on the ground
|
|
306
|
+
return [255, 245, 200] if inside?(u, v, [[0.46, 0.74], [0.56, 0.74], [0.57, 0.86], [0.45, 0.86]])
|
|
307
|
+
return [60, 45, 30] if inside?(u, v, [[0.44, 0.86], [0.58, 0.86], [0.58, 0.88], [0.44, 0.88]])
|
|
308
|
+
# other victims huddled around him (dark)
|
|
309
|
+
return [60, 45, 35] if ellipse(u, v, 0.22, 0.58, 0.06, 0.13) < 1
|
|
310
|
+
return [70, 55, 40] if ellipse(u, v, 0.43, 0.60, 0.04, 0.12) < 1
|
|
311
|
+
return [55, 45, 35] if ellipse(u, v, 0.20, 0.86, 0.12, 0.04) < 1
|
|
312
|
+
# the firing squad: a dark row seen from behind, rifles aimed left
|
|
313
|
+
return [40, 35, 30] if v > 0.54 && v < 0.565 && u > 0.47 && u < 0.62 && ((u * 60).floor % 3 != 0)
|
|
314
|
+
[0.62, 0.70, 0.78, 0.86, 0.94].each do |sx|
|
|
315
|
+
return [25, 25, 25] if ellipse(u, v, sx, 0.41, 0.03, 0.035) < 1 # shako
|
|
316
|
+
if ellipse(u, v, sx + 0.01, 0.62, 0.055, 0.20) < 1
|
|
317
|
+
return mix([90, 80, 60], [45, 40, 32], (u - sx + 0.05) * 8)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
return [45, 40, 32] if v > 0.55 && v < 0.85 && u > 0.6
|
|
321
|
+
# lit ground
|
|
322
|
+
ground = 0.70 - 0.25 * smooth(0.6, 0.0, u) * 0 # flat
|
|
323
|
+
if v > ground
|
|
324
|
+
return mix([60, 45, 30], [210, 175, 110], lamp)
|
|
325
|
+
end
|
|
326
|
+
# the dark hill at left, the town and the night sky
|
|
327
|
+
hill = 0.20 + u * 0.8
|
|
328
|
+
return mix([40, 35, 25], [110, 90, 60], lamp * 0.8) if v > hill
|
|
329
|
+
return [55, 50, 50] if v > 0.30 && (u > 0.66 && u < 0.72 || u > 0.74 && u < 0.85 && v > 0.36) # town buildings
|
|
330
|
+
return [50, 45, 45] if (u - 0.69).abs < 0.012 && v > 0.22 # church tower
|
|
331
|
+
mix([15, 15, 20], [35, 35, 40], v)
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Millet, The Gleaners
|
|
335
|
+
def the_gleaners(u, v)
|
|
336
|
+
# three peasant women in the foreground: two bent low, one half-rising
|
|
337
|
+
women = [
|
|
338
|
+
[0.22, 0.68, [55, 85, 150], [150, 125, 95], [80, 85, 105], false],
|
|
339
|
+
[0.48, 0.70, [195, 45, 40], [95, 105, 130], [140, 105, 70], false],
|
|
340
|
+
[0.75, 0.60, [105, 65, 40], [120, 105, 90], [100, 85, 70], true],
|
|
341
|
+
]
|
|
342
|
+
women.each do |cx, cy, scarf, top, skirt, upright|
|
|
343
|
+
if upright
|
|
344
|
+
return scarf if ellipse(u, v, cx - 0.04, cy - 0.17, 0.03, 0.04) < 1
|
|
345
|
+
return top if ellipse(u, v, cx, cy - 0.06, 0.05, 0.10) < 1
|
|
346
|
+
return [170, 130, 95] if (u - (cx - 0.05 + (v - 0.55) * 0.1)).abs < 0.01 && v > 0.55 && v < 0.76
|
|
347
|
+
return skirt if inside?(u, v, [[cx - 0.05, cy - 0.02], [cx + 0.05, cy - 0.02], [cx + 0.07, 0.90], [cx - 0.05, 0.90]])
|
|
348
|
+
else
|
|
349
|
+
# the back slopes down toward the head near the ground
|
|
350
|
+
x = u - cx
|
|
351
|
+
y = v - cy
|
|
352
|
+
a = -0.3
|
|
353
|
+
lx = x * Math.cos(a) + y * Math.sin(a)
|
|
354
|
+
ly = -x * Math.sin(a) + y * Math.cos(a)
|
|
355
|
+
return scarf if ellipse(u, v, cx - 0.14, cy + 0.07, 0.035, 0.05) < 1
|
|
356
|
+
return top if (lx / 0.12)**2 + (ly / 0.075)**2 < 1
|
|
357
|
+
return skirt if inside?(u, v, [[cx + 0.0, cy - 0.02], [cx + 0.10, cy + 0.02], [cx + 0.11, 0.93], [cx - 0.01, 0.93]])
|
|
358
|
+
return [170, 130, 95] if (u - (cx - 0.10)).abs < 0.014 && v > cy + 0.05 && v < 0.92 # arm to the stubble
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
# distant haystacks and the harvest
|
|
362
|
+
stack = lambda do |sx, w, h|
|
|
363
|
+
ellipse(u, v, sx, 0.46, w, h) < 1 && v < 0.46
|
|
364
|
+
end
|
|
365
|
+
return mix([200, 165, 100], [150, 115, 65], (u - 0.80) * 8) if stack.call(0.86, 0.06, 0.17)
|
|
366
|
+
return mix([200, 165, 100], [150, 115, 65], (u - 0.66) * 10) if stack.call(0.70, 0.04, 0.11)
|
|
367
|
+
return [175, 140, 85] if stack.call(0.58, 0.025, 0.06)
|
|
368
|
+
return [110, 85, 60] if v > 0.41 && v < 0.46 && noise((u * 70).floor, 1) > 0.85 # tiny workers
|
|
369
|
+
if v > 0.38
|
|
370
|
+
base = mix([225, 205, 145], [180, 145, 90], smooth(0.38, 0.52, v))
|
|
371
|
+
base = mix(base, [150, 115, 75], smooth(0.5, 0.95, v))
|
|
372
|
+
stub = Math.sin(u * 160 + v * 40) > 0.6 && v > 0.55 ? 0.85 : 1.0
|
|
373
|
+
return Color.scale(jitter(base, u, v, 10), stub)
|
|
374
|
+
end
|
|
375
|
+
# pale hazy sky
|
|
376
|
+
jitter(mix([215, 215, 200], [235, 225, 195], v * 3), u, v, 6, 8)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Millet, The Angelus
|
|
380
|
+
def the_angelus(u, v)
|
|
381
|
+
horizon = 0.52
|
|
382
|
+
# the man, head bowed, hat held at his chest
|
|
383
|
+
return [50, 40, 32] if ellipse(u, v, 0.31, 0.27, 0.032, 0.04) < 1
|
|
384
|
+
return [65, 55, 50] if inside?(u, v, [[0.26, 0.29], [0.35, 0.30], [0.38, 0.60], [0.24, 0.60]])
|
|
385
|
+
return [40, 32, 25] if ellipse(u, v, 0.34, 0.40, 0.03, 0.035) < 1 # hat in hands
|
|
386
|
+
return [55, 42, 35] if inside?(u, v, [[0.25, 0.60], [0.37, 0.60], [0.36, 0.86], [0.26, 0.86]])
|
|
387
|
+
# the woman, hands clasped, head bowed under a pale cap
|
|
388
|
+
return [175, 165, 145] if ellipse(u, v, 0.645, 0.30, 0.03, 0.03) < 1
|
|
389
|
+
return [95, 70, 60] if inside?(u, v, [[0.61, 0.32], [0.69, 0.32], [0.71, 0.54], [0.60, 0.54]])
|
|
390
|
+
return [110, 60, 45] if inside?(u, v, [[0.60, 0.54], [0.71, 0.54], [0.75, 0.86], [0.57, 0.86]])
|
|
391
|
+
# pitchfork stuck in the ground at left
|
|
392
|
+
return [55, 42, 30] if (u - 0.18 + (v - 0.5) * 0.06).abs < 0.007 && v > 0.36 && v < 0.86
|
|
393
|
+
# basket between them, wheelbarrow with sacks at right
|
|
394
|
+
return [115, 85, 50] if ellipse(u, v, 0.47, 0.84, 0.04, 0.03) < 1
|
|
395
|
+
return [140, 120, 85] if ellipse(u, v, 0.87, 0.63, 0.06, 0.045) < 1
|
|
396
|
+
return [70, 55, 40] if (v - 0.67).abs < 0.01 && u > 0.78 && u < 0.94
|
|
397
|
+
# church steeple on the horizon
|
|
398
|
+
return [85, 75, 80] if (u - 0.84).abs < 0.008 && v > 0.44 && v < horizon || (u - 0.84).abs < 0.03 && v > 0.49 && v < horizon
|
|
399
|
+
if v > horizon
|
|
400
|
+
furrow = Math.sin(v * 200 / (v - 0.4)) > 0.5 ? 0.85 : 1.0
|
|
401
|
+
base = mix([125, 105, 70], [60, 48, 32], (v - horizon) * 2.2)
|
|
402
|
+
return Color.scale(jitter(base, u, v, 10), furrow)
|
|
403
|
+
end
|
|
404
|
+
# warm evening sky, glowing at the horizon
|
|
405
|
+
sky = ramp(v / horizon, [125, 125, 115], [195, 175, 135], [245, 210, 140])
|
|
406
|
+
jitter(sky, u, v, 6, 8)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Whistler, Arrangement in Grey and Black No. 1
|
|
410
|
+
def whistlers_mother(u, v)
|
|
411
|
+
# the mother, seated in profile facing left
|
|
412
|
+
return [235, 235, 230] if ellipse(u, v, 0.61, 0.36, 0.045, 0.05) < 1 && v < 0.38 || ellipse(u, v, 0.63, 0.40, 0.035, 0.05) < 1 && u > 0.62 # white cap
|
|
413
|
+
return [225, 200, 180] if ellipse(u, v, 0.59, 0.41, 0.03, 0.04) < 1 # face
|
|
414
|
+
return [230, 230, 225] if ellipse(u, v, 0.53, 0.66, 0.035, 0.025) < 1 # handkerchief in the lap
|
|
415
|
+
body = inside?(u, v, [[0.57, 0.44], [0.66, 0.44], [0.72, 0.60], [0.86, 0.92], [0.44, 0.92], [0.48, 0.70], [0.56, 0.60]])
|
|
416
|
+
return [25, 25, 28] if body
|
|
417
|
+
return [40, 35, 30] if inside?(u, v, [[0.66, 0.50], [0.80, 0.48], [0.82, 0.70], [0.72, 0.70]]) # chair back
|
|
418
|
+
return [60, 50, 40] if inside?(u, v, [[0.38, 0.88], [0.50, 0.88], [0.50, 0.94], [0.38, 0.94]]) # footstool
|
|
419
|
+
# the curtain at left, dark with a pale pattern
|
|
420
|
+
if u < 0.22 + 0.01 * Math.sin(v * 20)
|
|
421
|
+
return [175, 170, 155] if noise((u * 60).floor, (v * 45).floor) > 0.82
|
|
422
|
+
return Math.sin(u * 80) > 0.6 ? [45, 42, 40] : [30, 28, 27]
|
|
423
|
+
end
|
|
424
|
+
# the framed picture on the wall
|
|
425
|
+
if u > 0.28 && u < 0.50 && v > 0.16 && v < 0.40
|
|
426
|
+
return [20, 20, 20] if u < 0.30 || u > 0.48 || v < 0.18 || v > 0.38
|
|
427
|
+
return [225, 222, 210] if u < 0.32 || u > 0.46 || v < 0.21 || v > 0.35
|
|
428
|
+
return mix([110, 110, 105], [170, 168, 160], vnoise(u, v, 30))
|
|
429
|
+
end
|
|
430
|
+
# floor and wall
|
|
431
|
+
return jitter([90, 85, 75], u, v, 8) if v > 0.88
|
|
432
|
+
return [60, 58, 55] if (v - 0.76).abs < 0.008
|
|
433
|
+
jitter([150, 148, 140], u, v, 5, 8)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Aivazovsky, The Ninth Wave
|
|
437
|
+
def ninth_wave(u, v)
|
|
438
|
+
sun_u = 0.55
|
|
439
|
+
sun_v = 0.38
|
|
440
|
+
glow = Math.exp(-(dist(u, v, sun_u, sun_v, 1.4) / 0.25)**2)
|
|
441
|
+
# survivors on the broken mast, lower left
|
|
442
|
+
mast_v = 0.80 - (u - 0.10) * 0.25
|
|
443
|
+
if u > 0.08 && u < 0.45 && (v - mast_v).abs < 0.018
|
|
444
|
+
return [45, 30, 20]
|
|
445
|
+
end
|
|
446
|
+
[0.16, 0.23, 0.31, 0.38].each do |fx|
|
|
447
|
+
mv = 0.80 - (fx - 0.10) * 0.25
|
|
448
|
+
return [60, 40, 35] if ellipse(u, v, fx, mv - 0.04, 0.018, 0.04) < 1
|
|
449
|
+
return [120, 50, 40] if ellipse(u, v, fx, mv - 0.085, 0.012, 0.015) < 1
|
|
450
|
+
end
|
|
451
|
+
# the great wave rising at left, lit through by the sun
|
|
452
|
+
crest = 0.55 - 0.25 * Math.exp(-((u - 0.25) / 0.17)**2)
|
|
453
|
+
sea = 0.58 + 0.03 * Math.sin(u * 18)
|
|
454
|
+
wave_top = [crest, sea].min
|
|
455
|
+
if v > wave_top
|
|
456
|
+
if v < wave_top + 0.035 && Math.sin(u * 60) > -0.3
|
|
457
|
+
return mix([250, 240, 200], [210, 220, 190], (v - wave_top) * 20) # foam crest
|
|
458
|
+
end
|
|
459
|
+
translucent = Math.exp(-((v - wave_top - 0.08) / 0.10)**2) * (0.5 + glow)
|
|
460
|
+
streak = Math.sin(u * 30 + v * 70 + Math.sin(u * 7) * 3)
|
|
461
|
+
base = mix([30, 70, 60], [140, 200, 120], translucent)
|
|
462
|
+
base = mix(base, [20, 40, 40], smooth(0.75, 1.0, v))
|
|
463
|
+
base = mix(base, [235, 230, 190], 0.5) if streak > 0.85
|
|
464
|
+
return jitter(base, u, v, 8)
|
|
465
|
+
end
|
|
466
|
+
# sky: sun glowing through stormy air
|
|
467
|
+
return [255, 245, 180] if dist(u, v, sun_u, sun_v, 1.4) < 0.045
|
|
468
|
+
sky = ramp(v / 0.6, [80, 60, 60], [200, 120, 80], [240, 180, 90])
|
|
469
|
+
sky = mix(sky, [255, 220, 120], glow)
|
|
470
|
+
cl = vnoise(u, v, 5)
|
|
471
|
+
sky = mix(sky, [110, 80, 80], (cl - 0.5) * 1.2) if cl > 0.5 && v < 0.35
|
|
472
|
+
jitter(sky, u, v, 8, 10)
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# Fragonard, The Swing
|
|
476
|
+
def the_swing(u, v)
|
|
477
|
+
# the woman in a billowing pink dress, the focus of the scene
|
|
478
|
+
dress = ellipse(u, v, 0.58, 0.52, 0.17, 0.10)
|
|
479
|
+
if dress < 1
|
|
480
|
+
frill = Math.sin(u * 50 + v * 40) > 0.6
|
|
481
|
+
return frill ? [255, 200, 205] : mix([235, 120, 140], [250, 160, 175], 1 - dress)
|
|
482
|
+
end
|
|
483
|
+
return [150, 115, 85] if ellipse(u, v, 0.66, 0.35, 0.04, 0.035) < 1 && u > 0.65 # hair
|
|
484
|
+
return [245, 210, 190] if ellipse(u, v, 0.65, 0.36, 0.035, 0.035) < 1 # head
|
|
485
|
+
return [225, 110, 130] if ellipse(u, v, 0.63, 0.42, 0.045, 0.04) < 1 # bodice
|
|
486
|
+
return [245, 215, 195] if (v - 0.52 - (u - 0.41) * 0.5).abs < 0.018 && u > 0.33 && u < 0.42 # leg kicked up-left
|
|
487
|
+
return [250, 130, 150] if ellipse(u, v, 0.27, 0.25, 0.035, 0.022) < 1 # the flying shoe
|
|
488
|
+
# swing ropes running up to the upper left
|
|
489
|
+
return [215, 190, 130] if (u - 0.50 + (0.47 - v) * 0.45).abs < 0.014 && v < 0.47
|
|
490
|
+
return [215, 190, 130] if (u - 0.66 + (0.44 - v) * 0.45).abs < 0.014 && v < 0.44
|
|
491
|
+
# the hidden admirer in the bushes at lower left, reaching up
|
|
492
|
+
return [245, 210, 185] if ellipse(u, v, 0.18, 0.76, 0.035, 0.03) < 1
|
|
493
|
+
return [225, 215, 180] if (v - 0.73 + (u - 0.22) * 1.1).abs < 0.018 && u > 0.21 && u < 0.33 # raised arm
|
|
494
|
+
return [190, 185, 150] if ellipse(u, v, 0.10, 0.82, 0.10, 0.04) < 1
|
|
495
|
+
# pale sky gap at top right
|
|
496
|
+
gap = ellipse(u, v, 0.92, 0.10, 0.18, 0.16)
|
|
497
|
+
return mix([225, 230, 215], [185, 200, 190], gap) if gap < 1 && vnoise(u, v, 12) < 0.7
|
|
498
|
+
# pool of warm light behind her, dark lush trees framing everything
|
|
499
|
+
light = Math.exp(-((u - 0.62) / 0.30)**2 - ((v - 0.48) / 0.28)**2)
|
|
500
|
+
leaf = vnoise(u, v, 16) * 0.6 + vnoise(u, v, 5) * 0.4
|
|
501
|
+
green = mix([15, 30, 15], [80, 105, 45], leaf * leaf)
|
|
502
|
+
return mix(green, [215, 190, 120], light * 1.1 - 0.2 + leaf * 0.2) if light > 0.3
|
|
503
|
+
jitter(mix(green, [120, 130, 70], light * 0.8), u, v, 8)
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# David, Napoleon Crossing the Alps
|
|
507
|
+
def napoleon_crossing_the_alps(u, v)
|
|
508
|
+
# the billowing gold cloak streaming up and to the right
|
|
509
|
+
cloak = inside?(u, v, [[0.50, 0.32], [0.62, 0.15], [0.80, 0.08], [0.92, 0.18], [0.80, 0.28], [0.70, 0.40], [0.56, 0.48]])
|
|
510
|
+
return mix([240, 170, 50], [180, 90, 30], vnoise(u, v, 12) * 1.2) if cloak
|
|
511
|
+
# the pointing arm
|
|
512
|
+
return [40, 45, 80] if (v - 0.30 + (0.45 - u) * 1.1).abs < 0.018 && u > 0.33 && u < 0.47
|
|
513
|
+
return [230, 200, 170] if ellipse(u, v, 0.32, 0.165, 0.015, 0.015) < 1
|
|
514
|
+
# bicorne hat and face
|
|
515
|
+
return [25, 25, 25] if ellipse(u, v, 0.49, 0.24, 0.06, 0.022) < 1
|
|
516
|
+
return [230, 195, 160] if ellipse(u, v, 0.49, 0.285, 0.025, 0.03) < 1
|
|
517
|
+
# rider's body
|
|
518
|
+
return [45, 50, 90] if inside?(u, v, [[0.45, 0.31], [0.54, 0.31], [0.57, 0.48], [0.45, 0.50]])
|
|
519
|
+
return [210, 200, 170] if inside?(u, v, [[0.47, 0.48], [0.57, 0.48], [0.62, 0.58], [0.52, 0.58]]) # breeches
|
|
520
|
+
return [30, 25, 20] if ellipse(u, v, 0.62, 0.62, 0.025, 0.05) < 1 # boot
|
|
521
|
+
# the rearing white horse on a diagonal
|
|
522
|
+
return [70, 60, 50] if ellipse(u, v, 0.24, 0.40, 0.04, 0.10) < 1 && u < 0.26 # mane
|
|
523
|
+
horse = ellipse(u, v, 0.47, 0.56, 0.20, 0.09) < 1 ||
|
|
524
|
+
inside?(u, v, [[0.18, 0.36], [0.26, 0.30], [0.34, 0.48], [0.30, 0.56], [0.20, 0.44]]) || # neck and head
|
|
525
|
+
ellipse(u, v, 0.15, 0.40, 0.05, 0.04) < 1 ||
|
|
526
|
+
(u - 0.26 - (v - 0.56) * 0.6).abs < 0.02 && v > 0.56 && v < 0.70 || # forelegs raised
|
|
527
|
+
(u - 0.62 - (v - 0.62) * 0.3).abs < 0.025 && v > 0.62 && v < 0.92 ||
|
|
528
|
+
(u - 0.70 - (v - 0.62) * 0.1).abs < 0.025 && v > 0.60 && v < 0.94
|
|
529
|
+
if horse
|
|
530
|
+
return mix([245, 240, 230], [170, 165, 160], (v - 0.35) * 1.5)
|
|
531
|
+
end
|
|
532
|
+
return [80, 65, 55] if ellipse(u, v, 0.74, 0.60, 0.05, 0.10) < 1 && u > 0.66 # tail
|
|
533
|
+
# rocks at the bottom
|
|
534
|
+
return jitter(mix([90, 80, 65], [50, 45, 40], (v - 0.85) * 5), u, v, 12) if v > 0.88 - 0.05 * Math.sin(u * 4)
|
|
535
|
+
# mountains and dark stormy sky
|
|
536
|
+
return mix([110, 105, 100], [70, 70, 75], vnoise(u, v, 8)) if v > 0.70 - 0.25 * u.clamp(0, 1) * (u < 0.5 ? 1 : -1) + 0.0 && v > 0.55
|
|
537
|
+
sky = mix([60, 55, 55], [140, 130, 115], vnoise(u, v, 4) * 0.8 + (1 - v) * 0.2)
|
|
538
|
+
jitter(sky, u, v, 10, 10)
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
# Gainsborough, The Blue Boy
|
|
542
|
+
def the_blue_boy(u, v)
|
|
543
|
+
blue = lambda do
|
|
544
|
+
sheen = Math.sin(u * 50 + v * 20 + vnoise(u, v, 10) * 6)
|
|
545
|
+
mix([40, 80, 140], [160, 200, 230], sheen * 0.5 + 0.5)
|
|
546
|
+
end
|
|
547
|
+
# head and hair
|
|
548
|
+
return [230, 200, 175] if ellipse(u, v, 0.50, 0.18, 0.045, 0.045) < 1
|
|
549
|
+
return [110, 75, 45] if ellipse(u, v, 0.50, 0.17, 0.065, 0.06) < 1
|
|
550
|
+
# lace collar
|
|
551
|
+
return [240, 238, 230] if ellipse(u, v, 0.50, 0.245, 0.07, 0.025) < 1
|
|
552
|
+
# the blue satin suit
|
|
553
|
+
return blue.call if inside?(u, v, [[0.42, 0.25], [0.58, 0.25], [0.63, 0.40], [0.62, 0.62], [0.38, 0.62], [0.37, 0.40]])
|
|
554
|
+
# cape on his left side (viewer's right) and the arm on the hip
|
|
555
|
+
return Color.scale(blue.call, 0.75) if inside?(u, v, [[0.58, 0.25], [0.68, 0.32], [0.70, 0.56], [0.62, 0.55]])
|
|
556
|
+
# the hat in his hand at the viewer's left
|
|
557
|
+
return blue.call if (u - 0.36).abs < 0.025 && v > 0.27 && v < 0.50 # arm
|
|
558
|
+
return [45, 35, 30] if ellipse(u, v, 0.33, 0.55, 0.05, 0.035) < 1
|
|
559
|
+
# legs in blue stockings and shoes
|
|
560
|
+
if v >= 0.62 && v < 0.90
|
|
561
|
+
return blue.call if (u - 0.45).abs < 0.03 || (u - 0.555).abs < 0.03
|
|
562
|
+
end
|
|
563
|
+
return [70, 55, 45] if v >= 0.90 && v < 0.93 && ((u - 0.45).abs < 0.04 || (u - 0.56).abs < 0.04)
|
|
564
|
+
# brownish landscape and cloudy sky
|
|
565
|
+
ground = 0.75 + 0.05 * Math.sin(u * 5)
|
|
566
|
+
return jitter(mix([95, 75, 50], [55, 45, 30], (v - ground) * 3), u, v, 12) if v > ground
|
|
567
|
+
return jitter(mix([60, 60, 45], [100, 90, 65], vnoise(u, v, 10)), u, v, 10) if v > 0.55 + 0.1 * Math.sin(u * 4) || u < 0.2 && v > 0.3
|
|
568
|
+
sky = mix([90, 110, 125], [180, 175, 155], vnoise(u, v, 5) + v * 0.3)
|
|
569
|
+
jitter(sky, u, v, 10, 10)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
# Millais, Ophelia
|
|
573
|
+
def ophelia(u, v)
|
|
574
|
+
# Ophelia floating on her back, face upward at left, dress spread to the right
|
|
575
|
+
return [240, 220, 205] if ellipse(u, v, 0.33, 0.58, 0.035, 0.035) < 1 # face
|
|
576
|
+
return [130, 70, 40] if ellipse(u, v, 0.30, 0.58, 0.07, 0.05) < 1 # auburn hair in the water
|
|
577
|
+
return [235, 215, 200] if ellipse(u, v, 0.40, 0.51, 0.015, 0.02) < 1 || ellipse(u, v, 0.43, 0.66, 0.015, 0.02) < 1 # open hands
|
|
578
|
+
dress = ellipse(u, v, 0.55, 0.59, 0.17, 0.07)
|
|
579
|
+
if dress < 1
|
|
580
|
+
sheen = Math.sin(u * 60 + v * 30) > 0.3
|
|
581
|
+
return sheen ? [200, 200, 185] : [150, 155, 140]
|
|
582
|
+
end
|
|
583
|
+
# scattered flowers on the water
|
|
584
|
+
[[0.47, 0.46, [200, 40, 40]], [0.62, 0.70, [230, 220, 120]], [0.75, 0.52, [210, 90, 150]], [0.25, 0.67, [220, 230, 240]], [0.82, 0.66, [200, 40, 40]]].each do |fx, fy, col|
|
|
585
|
+
return col if dist(u, v, fx, fy, 1.4) < 0.018
|
|
586
|
+
end
|
|
587
|
+
# the dark stream
|
|
588
|
+
bank_top = 0.40 + 0.05 * Math.sin(u * 7)
|
|
589
|
+
bank_bot = 0.80 - 0.04 * Math.sin(u * 5 + 1)
|
|
590
|
+
if v > bank_top && v < bank_bot
|
|
591
|
+
return mix([25, 40, 30], [55, 75, 55], vnoise(u, v, 12) * 0.8)
|
|
592
|
+
end
|
|
593
|
+
# dense green riverbank vegetation
|
|
594
|
+
leaf = vnoise(u, v, 18) * 0.6 + vnoise(u, v, 6) * 0.4
|
|
595
|
+
g = mix([25, 50, 20], [130, 160, 60], leaf * leaf * 1.5)
|
|
596
|
+
return [200, 60, 60] if v < bank_top && noise((u * 60).floor, (v * 40).floor) > 0.988
|
|
597
|
+
return [225, 225, 205] if noise((u * 60).floor + 7, (v * 40).floor) > 0.985
|
|
598
|
+
g
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
# Waterhouse, The Lady of Shalott
|
|
602
|
+
def the_lady_of_shalott(u, v)
|
|
603
|
+
# the lady in white, sitting upright with red hair
|
|
604
|
+
return [235, 205, 185] if ellipse(u, v, 0.40, 0.37, 0.025, 0.035) < 1
|
|
605
|
+
return [190, 90, 40] if ellipse(u, v, 0.41, 0.40, 0.04, 0.08) < 1 && u > 0.40
|
|
606
|
+
return [190, 90, 40] if ellipse(u, v, 0.40, 0.35, 0.035, 0.035) < 1
|
|
607
|
+
return jitter([235, 235, 225], u, v, 10) if inside?(u, v, [[0.37, 0.40], [0.45, 0.40], [0.50, 0.62], [0.33, 0.62]])
|
|
608
|
+
# candles and lantern at the prow (left)
|
|
609
|
+
[0.14, 0.17, 0.20].each do |cx|
|
|
610
|
+
return [255, 220, 120] if (u - cx).abs < 0.006 && v > 0.53 && v < 0.55
|
|
611
|
+
return [230, 225, 200] if (u - cx).abs < 0.006 && v > 0.55 && v < 0.61
|
|
612
|
+
end
|
|
613
|
+
return [80, 60, 30] if (u - 0.10).abs < 0.006 && v > 0.40 && v < 0.62
|
|
614
|
+
return [240, 180, 70] if ellipse(u, v, 0.10, 0.42, 0.015, 0.02) < 1
|
|
615
|
+
# the wooden boat with a patterned tapestry draped over the side
|
|
616
|
+
if inside?(u, v, [[0.06, 0.60], [0.85, 0.62], [0.80, 0.70], [0.12, 0.70]])
|
|
617
|
+
if u > 0.25 && u < 0.62
|
|
618
|
+
pat = (u * 40).floor.even? ^ (v * 40).floor.even?
|
|
619
|
+
return pat ? [150, 50, 40] : [190, 150, 70]
|
|
620
|
+
end
|
|
621
|
+
return [90, 60, 35]
|
|
622
|
+
end
|
|
623
|
+
return [150, 50, 40] if u > 0.28 && u < 0.58 && v > 0.70 && v < 0.78 && ((u * 40).floor.even? ^ (v * 40).floor.even?) # tapestry trailing in water
|
|
624
|
+
# reeds in the foreground
|
|
625
|
+
if v > 0.62 && (u < 0.10 || u > 0.88)
|
|
626
|
+
return [110, 115, 60] if Math.sin(u * 140) > 0.2
|
|
627
|
+
end
|
|
628
|
+
# dark water
|
|
629
|
+
if v > 0.58
|
|
630
|
+
refl = Math.sin(v * 100 + Math.sin(u * 13) * 2) > 0.7
|
|
631
|
+
return refl ? [70, 85, 85] : [25, 35, 35]
|
|
632
|
+
end
|
|
633
|
+
# far bank and autumn trees
|
|
634
|
+
return mix([70, 80, 40], [100, 110, 50], vnoise(u, v, 20)) if v > 0.48
|
|
635
|
+
leaf = vnoise(u, v, 10)
|
|
636
|
+
return mix([110, 90, 40], [190, 140, 60], leaf) if v > 0.12 + 0.05 * Math.sin(u * 9) || leaf > 0.6
|
|
637
|
+
jitter([175, 185, 180], u, v, 8)
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
# Leutze, Washington Crossing the Delaware
|
|
641
|
+
def washington_crossing_the_delaware(u, v)
|
|
642
|
+
# the flag held up behind Washington, slanting back
|
|
643
|
+
if inside?(u, v, [[0.53, 0.10], [0.68, 0.15], [0.66, 0.38], [0.55, 0.34]])
|
|
644
|
+
return [40, 50, 115] if u < 0.585 && v < 0.21
|
|
645
|
+
return ((v * 32).floor.even? ? [200, 40, 45] : [240, 235, 225])
|
|
646
|
+
end
|
|
647
|
+
return [70, 55, 40] if (u - 0.53 - (v - 0.10) * 0.15).abs < 0.007 && v > 0.08 && v < 0.62
|
|
648
|
+
# Washington standing at the bow
|
|
649
|
+
return [40, 35, 30] if ellipse(u, v, 0.42, 0.175, 0.032, 0.016) < 1 # tricorn hat
|
|
650
|
+
return [225, 195, 165] if ellipse(u, v, 0.42, 0.215, 0.018, 0.032) < 1
|
|
651
|
+
return [75, 70, 85] if inside?(u, v, [[0.39, 0.25], [0.46, 0.25], [0.48, 0.40], [0.46, 0.58], [0.38, 0.58], [0.37, 0.40]])
|
|
652
|
+
return [230, 215, 180] if inside?(u, v, [[0.40, 0.42], [0.45, 0.42], [0.45, 0.58], [0.40, 0.58]]) # breeches
|
|
653
|
+
# the men rowing, crowded into the boat
|
|
654
|
+
[0.18, 0.25, 0.32, 0.51, 0.58, 0.65, 0.72].each_with_index do |mx, i|
|
|
655
|
+
top = 0.42 + (i.even? ? 0.03 : 0.0)
|
|
656
|
+
return [150, 45, 40] if i == 2 && ellipse(u, v, mx, top + 0.07, 0.03, 0.07) < 1
|
|
657
|
+
return [200, 170, 140] if ellipse(u, v, mx, top - 0.01, 0.015, 0.025) < 1
|
|
658
|
+
return [35, 35, 40] if ellipse(u, v, mx, top - 0.035, 0.022, 0.012) < 1
|
|
659
|
+
return [60, 55, 55] if ellipse(u, v, mx, top + 0.09, 0.03, 0.10) < 1
|
|
660
|
+
end
|
|
661
|
+
# oars and poles
|
|
662
|
+
return [90, 70, 45] if (v - 0.60 - (u - 0.12) * 0.6).abs < 0.008 && u > 0.08 && u < 0.24
|
|
663
|
+
return [90, 70, 45] if (v - 0.64 + (u - 0.82) * 0.6).abs < 0.008 && u > 0.76 && u < 0.88
|
|
664
|
+
# the boat
|
|
665
|
+
return [65, 48, 35] if inside?(u, v, [[0.10, 0.58], [0.82, 0.57], [0.78, 0.70], [0.16, 0.70]])
|
|
666
|
+
# ice floes on the river
|
|
667
|
+
if v > 0.54
|
|
668
|
+
f = vnoise(u * 1.5, v * 3, 7)
|
|
669
|
+
return mix([230, 235, 240], [185, 195, 205], (v - 0.54) * 2) if f > 0.6
|
|
670
|
+
return mix([75, 90, 105], [40, 55, 70], (v - 0.54) * 2)
|
|
671
|
+
end
|
|
672
|
+
# far shore with other boats
|
|
673
|
+
return [45, 50, 55] if v > 0.51 - 0.01 * Math.sin(u * 20)
|
|
674
|
+
return [55, 55, 60] if v > 0.47 && (u - 0.88).abs < 0.06 && v > 0.49 - (0.06 - (u - 0.88).abs) * 0.3
|
|
675
|
+
# dawn sky
|
|
676
|
+
sky = ramp(v / 0.52, [80, 90, 110], [150, 150, 150], [250, 225, 160])
|
|
677
|
+
c = vnoise(u, v, 6)
|
|
678
|
+
sky = mix(sky, [60, 65, 80], (c - 0.55) * 1.8) if c > 0.55 && v < 0.35
|
|
679
|
+
jitter(sky, u, v, 6, 10)
|
|
680
|
+
end
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
piece(:wanderer_above_the_sea_of_fog, "Wanderer above the Sea of Fog", "Caspar David Friedrich", "c. 1818", aspect: 0.79) { |u, v| wanderer_above_the_sea_of_fog(u, v) }
|
|
684
|
+
piece(:monk_by_the_sea, "The Monk by the Sea", "Caspar David Friedrich", "c. 1810", aspect: 1.56) { |u, v| monk_by_the_sea(u, v) }
|
|
685
|
+
piece(:sea_of_ice, "The Sea of Ice", "Caspar David Friedrich", "1824", aspect: 1.31) { |u, v| sea_of_ice(u, v) }
|
|
686
|
+
piece(:fighting_temeraire, "The Fighting Temeraire", "J. M. W. Turner", "1839", aspect: 1.33) { |u, v| fighting_temeraire(u, v) }
|
|
687
|
+
piece(:rain_steam_and_speed, "Rain, Steam and Speed", "J. M. W. Turner", "1844", aspect: 1.34) { |u, v| rain_steam_and_speed(u, v) }
|
|
688
|
+
piece(:hay_wain, "The Hay Wain", "John Constable", "1821", aspect: 1.42) { |u, v| hay_wain(u, v) }
|
|
689
|
+
piece(:liberty_leading_the_people, "Liberty Leading the People", "Eugène Delacroix", "1830", aspect: 1.25) { |u, v| liberty_leading_the_people(u, v) }
|
|
690
|
+
piece(:raft_of_the_medusa, "The Raft of the Medusa", "Théodore Géricault", "1819", aspect: 1.46) { |u, v| raft_of_the_medusa(u, v) }
|
|
691
|
+
piece(:third_of_may, "The Third of May 1808", "Francisco Goya", "1814", aspect: 1.30) { |u, v| third_of_may(u, v) }
|
|
692
|
+
piece(:the_gleaners, "The Gleaners", "Jean-François Millet", "1857", aspect: 1.33) { |u, v| the_gleaners(u, v) }
|
|
693
|
+
piece(:the_angelus, "The Angelus", "Jean-François Millet", "1859", aspect: 1.19) { |u, v| the_angelus(u, v) }
|
|
694
|
+
piece(:whistlers_mother, "Whistler's Mother", "James McNeill Whistler", "1871", aspect: 1.13) { |u, v| whistlers_mother(u, v) }
|
|
695
|
+
piece(:ninth_wave, "The Ninth Wave", "Ivan Aivazovsky", "1850", aspect: 1.50) { |u, v| ninth_wave(u, v) }
|
|
696
|
+
piece(:the_swing, "The Swing", "Jean-Honoré Fragonard", "1767", aspect: 0.79) { |u, v| the_swing(u, v) }
|
|
697
|
+
piece(:napoleon_crossing_the_alps, "Napoleon Crossing the Alps", "Jacques-Louis David", "1801", aspect: 0.85) { |u, v| napoleon_crossing_the_alps(u, v) }
|
|
698
|
+
piece(:the_blue_boy, "The Blue Boy", "Thomas Gainsborough", "c. 1770", aspect: 0.68) { |u, v| the_blue_boy(u, v) }
|
|
699
|
+
piece(:ophelia, "Ophelia", "John Everett Millais", "1852", aspect: 1.47) { |u, v| ophelia(u, v) }
|
|
700
|
+
piece(:the_lady_of_shalott, "The Lady of Shalott", "John William Waterhouse", "1888", aspect: 1.31) { |u, v| the_lady_of_shalott(u, v) }
|
|
701
|
+
piece(:washington_crossing_the_delaware, "Washington Crossing the Delaware", "Emanuel Leutze", "1851", aspect: 1.71) { |u, v| washington_crossing_the_delaware(u, v) }
|
|
702
|
+
end
|
|
703
|
+
end
|