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,774 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dopairb
|
|
4
|
+
module Gallery
|
|
5
|
+
# Shared shape helpers for the pieces in this file.
|
|
6
|
+
module ModernAncient
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Distance from (u, v) to the segment (x1, y1)-(x2, y2); sx scales the u axis.
|
|
10
|
+
def seg(u, v, x1, y1, x2, y2, sx = 1.0)
|
|
11
|
+
px = (u - x1) * sx
|
|
12
|
+
py = v - y1
|
|
13
|
+
dx = (x2 - x1) * sx
|
|
14
|
+
dy = y2 - y1
|
|
15
|
+
t = ((px * dx + py * dy) / (dx * dx + dy * dy + 1e-12)).clamp(0.0, 1.0)
|
|
16
|
+
Math.hypot(px - dx * t, py - dy * t)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Position along an Archimedean spiral arm, 0..1 (lines where it is small).
|
|
20
|
+
def spiral(dx, dy, pitch, dir = 1)
|
|
21
|
+
t = Math.hypot(dx, dy) / pitch - dir * Math.atan2(dy, dx) / (2 * Math::PI)
|
|
22
|
+
t - t.floor
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Triangle wave 0..1..0 with period 1.
|
|
26
|
+
def tri(x) = 1 - (x - x.floor - 0.5).abs * 2
|
|
27
|
+
|
|
28
|
+
# A cave-painting horse facing left; s scales it, sx is the aspect.
|
|
29
|
+
# => :body, :mane (dark parts) or nil
|
|
30
|
+
def horse(u, v, x, y, s, sx)
|
|
31
|
+
return :body if Paint.ellipse(u * sx, v, x * sx, y, 0.13 * s, 0.065 * s) < 1
|
|
32
|
+
return :mane if seg(u, v, x - 0.045 * s, y - 0.03 * s, x - 0.075 * s, y - 0.085 * s, sx) < 0.028 * s
|
|
33
|
+
return :mane if Paint.ellipse(u * sx, v, (x - 0.085 * s) * sx, y - 0.085 * s, 0.045 * s, 0.025 * s) < 1
|
|
34
|
+
[[-0.05, -0.07], [-0.02, -0.04], [0.04, 0.02], [0.06, 0.08]].each do |a, b|
|
|
35
|
+
return :mane if seg(u, v, x + a * s, y + 0.03 * s, x + b * s, y + 0.13 * s, sx) < 0.012 * s
|
|
36
|
+
end
|
|
37
|
+
return :mane if seg(u, v, x + 0.07 * s, y - 0.02 * s, x + 0.10 * s, y + 0.07 * s, sx) < 0.012 * s
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# A tree of the Nebamun garden: trunk at the base, crown pointing away.
|
|
42
|
+
def nebamun_tree(u, v, along, across, palm)
|
|
43
|
+
return nil if along < -0.01 || along > 0.25 || across.abs > 0.08
|
|
44
|
+
return [110, 70, 40] if along < 0.09 && across.abs < 0.008
|
|
45
|
+
if palm
|
|
46
|
+
return [180, 50, 40] if (across / 0.02)**2 + ((along - 0.10) / 0.02)**2 < 1
|
|
47
|
+
fx = along - 0.09
|
|
48
|
+
return nil if fx < 0 || fx > 0.13
|
|
49
|
+
a = Math.atan2(across, fx)
|
|
50
|
+
return [40, 90, 50] if a.abs < 1.1 && Math.sin(a * 9).abs > 0.4
|
|
51
|
+
return nil
|
|
52
|
+
end
|
|
53
|
+
ce = (across / 0.055)**2 + ((along - 0.15) / 0.07)**2
|
|
54
|
+
return nil if ce >= 1
|
|
55
|
+
return [200, 60, 40] if Paint.noise((u * 60).floor, (v * 60).floor) > 0.82
|
|
56
|
+
ce > 0.75 ? [30, 70, 40] : [60, 115, 60]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# A Bayeux figure at x; kind :seated, :oath or :stand.
|
|
60
|
+
def bayeux_figure(u, v, x, c, kind, sx)
|
|
61
|
+
olive = [120, 125, 65]
|
|
62
|
+
y0 = kind == :seated ? 0.40 : 0.36
|
|
63
|
+
return [205, 170, 140] if Paint.dist(u, v, x, y0, sx) < 0.045
|
|
64
|
+
return c if seg(u, v, x, y0 + 0.06, x, y0 + 0.28, sx) < 0.05 && v < y0 + 0.32
|
|
65
|
+
case kind
|
|
66
|
+
when :seated
|
|
67
|
+
return olive if (u - x).abs < 0.05 && v.between?(0.55, 0.72) && (u > x + 0.02 || v > 0.66)
|
|
68
|
+
return c if seg(u, v, x, 0.66, x + 0.04, 0.78, sx) < 0.012
|
|
69
|
+
return [80, 70, 60] if seg(u, v, x + 0.02, 0.52, x + 0.06, 0.34, sx) < 0.006 # sword
|
|
70
|
+
when :oath
|
|
71
|
+
return c if seg(u, v, x, y0 + 0.10, x - 0.06, y0 + 0.16, sx) < 0.013
|
|
72
|
+
return c if seg(u, v, x, y0 + 0.10, x + 0.06, y0 + 0.16, sx) < 0.013
|
|
73
|
+
else
|
|
74
|
+
return c if seg(u, v, x, y0 + 0.10, x - 0.04, y0 + 0.04, sx) < 0.012
|
|
75
|
+
end
|
|
76
|
+
return c if v > y0 + 0.25 && ((u - x).abs - 0.008).abs < 0.006 && v < 0.79
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Klimt, The Kiss
|
|
82
|
+
piece :the_kiss, "The Kiss", "Gustav Klimt", "1908", aspect: 1.0 do |u, v|
|
|
83
|
+
# her face, tilted on his shoulder, eyes closed
|
|
84
|
+
if ellipse(u, v, 0.575, 0.31, 0.055, 0.042) < 1
|
|
85
|
+
next [120, 80, 60] if (v - 0.302 - (u - 0.56) * 0.3).abs < 0.006 && (u - 0.555).abs < 0.016
|
|
86
|
+
next [200, 90, 80] if ellipse(u, v, 0.605, 0.325, 0.01, 0.006) < 1
|
|
87
|
+
next mix([248, 228, 205], [225, 190, 160], u - 0.55)
|
|
88
|
+
end
|
|
89
|
+
# her hair with flowers
|
|
90
|
+
if ellipse(u, v, 0.635, 0.285, 0.045, 0.075) < 1
|
|
91
|
+
next noise((u * 90).floor, (v * 90).floor) > 0.7 ? [235, 120, 90] : [120, 60, 30]
|
|
92
|
+
end
|
|
93
|
+
# his cheek and dark hair crowned with ivy
|
|
94
|
+
next [210, 160, 120] if ellipse(u, v, 0.515, 0.22, 0.04, 0.05) < 1
|
|
95
|
+
if ellipse(u, v, 0.47, 0.15, 0.07, 0.06) < 1
|
|
96
|
+
next noise((u * 70).floor, (v * 70).floor) > 0.75 ? [90, 130, 60] : [25, 22, 20]
|
|
97
|
+
end
|
|
98
|
+
# her hand on his neck, her feet over the edge
|
|
99
|
+
next [240, 210, 185] if ellipse(u, v, 0.505, 0.27, 0.022, 0.03) < 1
|
|
100
|
+
next [235, 200, 170] if ellipse(u, v, 0.585, 0.935, 0.035, 0.022) < 1
|
|
101
|
+
robe = [[0.38, 0.10], [0.58, 0.10], [0.72, 0.26], [0.77, 0.50], [0.72, 0.74], [0.63, 0.92],
|
|
102
|
+
[0.50, 0.90], [0.33, 0.74], [0.26, 0.48], [0.29, 0.22]]
|
|
103
|
+
if inside?(u, v, robe)
|
|
104
|
+
if u < 0.55 - (v - 0.3) * 0.15
|
|
105
|
+
# his robe: black, white and silver rectangles on gold
|
|
106
|
+
cx = (u * 18).floor
|
|
107
|
+
cy = (v * 9).floor
|
|
108
|
+
lx = u * 18 - cx
|
|
109
|
+
ly = v * 9 - cy
|
|
110
|
+
n = noise(cx, cy)
|
|
111
|
+
if lx.between?(0.15, 0.85) && ly.between?(0.08, 0.92) && n > 0.35
|
|
112
|
+
next n > 0.75 ? [235, 230, 215] : (n > 0.55 ? [20, 18, 18] : [160, 160, 150])
|
|
113
|
+
end
|
|
114
|
+
next jitter([220, 175, 60], u, v, 20, 40)
|
|
115
|
+
end
|
|
116
|
+
# her dress: colourful circles
|
|
117
|
+
cx = (u * 18).floor
|
|
118
|
+
cy = (v * 18).floor
|
|
119
|
+
n = noise(cx + 7, cy)
|
|
120
|
+
if Math.hypot(u * 18 - cx - 0.5, v * 18 - cy - 0.5) < 0.38
|
|
121
|
+
next [[200, 50, 50], [70, 90, 170], [150, 70, 140], [240, 140, 60], [235, 235, 220]][(n * 5).floor]
|
|
122
|
+
end
|
|
123
|
+
next jitter([210, 165, 60], u, v, 20, 40)
|
|
124
|
+
end
|
|
125
|
+
# flower meadow at the bottom left, dropping to the right
|
|
126
|
+
meadow = [[0.0, 0.66], [0.25, 0.68], [0.50, 0.80], [0.64, 0.90], [0.70, 1.0], [0.0, 1.0]]
|
|
127
|
+
if inside?(u, v, meadow)
|
|
128
|
+
f = noise((u * 50).floor, (v * 50).floor)
|
|
129
|
+
next [[230, 90, 80], [240, 200, 80], [150, 110, 190], [240, 240, 230]][(f * 40).floor % 4] if f > 0.72
|
|
130
|
+
next jitter([60, 100, 55], u, v, 18, 25)
|
|
131
|
+
end
|
|
132
|
+
next [215, 175, 80] if noise((u * 64).floor, (v * 64).floor) > 0.88
|
|
133
|
+
mix([140, 115, 50], [95, 80, 40], vnoise(u, v, 6))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Klimt, The Tree of Life (Stoclet Frieze)
|
|
137
|
+
piece :tree_of_life, "The Tree of Life", "Gustav Klimt", "1909", aspect: 1.9 do |u, v|
|
|
138
|
+
x = u * 1.9
|
|
139
|
+
brown = [140, 90, 35]
|
|
140
|
+
gold = [205, 155, 50]
|
|
141
|
+
# the black bird on top of the tree
|
|
142
|
+
next [15, 15, 15] if ellipse(x, v, 0.95, 0.20, 0.035, 0.05) < 1 || ModernAncient.seg(x, v, 0.98, 0.16, 1.02, 0.15) < 0.01
|
|
143
|
+
# trunk with eye motifs
|
|
144
|
+
tw = 0.035 + (v - 0.4) * 0.05
|
|
145
|
+
if v > 0.26 && (x - 0.95).abs < tw
|
|
146
|
+
next [235, 225, 200] if ellipse(x, v, 0.95, ((v * 10).floor + 0.5) / 10, 0.015, 0.025) < 1 && v > 0.5
|
|
147
|
+
next mix(brown, gold, Math.sin(v * 60) * 0.5 + 0.5)
|
|
148
|
+
end
|
|
149
|
+
# spiralling branches: scrolls hanging off two curving boughs
|
|
150
|
+
scroll = nil
|
|
151
|
+
[[0.20, 0.30, 1], [0.45, 0.22, -1], [0.70, 0.36, 1], [0.30, 0.62, -1], [0.58, 0.55, 1],
|
|
152
|
+
[1.25, 0.30, -1], [1.48, 0.22, 1], [1.72, 0.36, -1], [1.62, 0.62, 1], [1.35, 0.55, -1],
|
|
153
|
+
[0.10, 0.52, 1], [1.82, 0.55, -1], [0.82, 0.62, -1], [1.10, 0.66, 1]].each do |cx, cy, dir|
|
|
154
|
+
dx = x - cx
|
|
155
|
+
dy = v - cy
|
|
156
|
+
r = Math.hypot(dx, dy)
|
|
157
|
+
next if r > 0.14
|
|
158
|
+
if ModernAncient.spiral(dx, dy, 0.05, dir) < 0.5
|
|
159
|
+
scroll = mix(gold, [100, 60, 25], r * 7)
|
|
160
|
+
break
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
next scroll if scroll
|
|
164
|
+
ax = (x - 0.95).abs
|
|
165
|
+
bough = 0.42 + 0.10 * Math.sin(ax * 7)
|
|
166
|
+
next brown if (v - bough).abs < 0.022 && ax < 0.9
|
|
167
|
+
bough2 = 0.30 + 0.12 * Math.sin(ax * 5 + 1)
|
|
168
|
+
next brown if (v - bough2).abs < 0.018 && ax.between?(0.05, 0.85)
|
|
169
|
+
# flowers on the ground
|
|
170
|
+
if v > 0.86
|
|
171
|
+
n = noise((u * 70).floor, (v * 40).floor)
|
|
172
|
+
next [[200, 70, 60], [90, 120, 170], [230, 200, 90]][(n * 30).floor % 3] if n > 0.7
|
|
173
|
+
next jitter([200, 175, 110], u, v, 12, 30)
|
|
174
|
+
end
|
|
175
|
+
jitter(mix([238, 225, 185], [225, 200, 140], v), u, v, 10, 15)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Klimt, Portrait of Adele Bloch-Bauer I
|
|
179
|
+
piece :portrait_of_adele_bloch_bauer, "Portrait of Adele Bloch-Bauer I", "Gustav Klimt", "1907", aspect: 1.0 do |u, v|
|
|
180
|
+
skin = [238, 222, 210]
|
|
181
|
+
# face
|
|
182
|
+
fe = ellipse(u, v, 0.50, 0.25, 0.055, 0.075)
|
|
183
|
+
if fe < 1
|
|
184
|
+
next [40, 30, 30] if (v - 0.235).abs < 0.008 && ((u - 0.50).abs - 0.022).abs < 0.012
|
|
185
|
+
next [190, 70, 70] if ellipse(u, v, 0.50, 0.295, 0.015, 0.007) < 1
|
|
186
|
+
next mix(skin, [210, 185, 175], fe)
|
|
187
|
+
end
|
|
188
|
+
# black hair
|
|
189
|
+
next [25, 20, 22] if ellipse(u, v, 0.50, 0.19, 0.08, 0.08) < 1 && v < 0.26
|
|
190
|
+
# neck with golden choker
|
|
191
|
+
if (u - 0.50).abs < 0.028 && v.between?(0.30, 0.37)
|
|
192
|
+
next v.between?(0.32, 0.345) ? [230, 190, 80] : skin
|
|
193
|
+
end
|
|
194
|
+
# pale shoulders and clasped hands
|
|
195
|
+
next skin if ellipse(u, v, 0.50, 0.40, 0.13, 0.04) < 1
|
|
196
|
+
if ellipse(u, v, 0.47, 0.50, 0.07, 0.035) < 1
|
|
197
|
+
next ((u * 60).floor % 3).zero? ? [200, 175, 165] : skin
|
|
198
|
+
end
|
|
199
|
+
# green patch at the lower left
|
|
200
|
+
if u < 0.26 + 0.05 * Math.sin(v * 10) && v > 0.74
|
|
201
|
+
next noise((u * 50).floor, (v * 50).floor) > 0.8 ? [210, 180, 80] : jitter([70, 120, 75], u, v, 15, 20)
|
|
202
|
+
end
|
|
203
|
+
dress = [[0.36, 0.38], [0.64, 0.38], [0.70, 0.70], [0.80, 1.0], [0.22, 1.0], [0.30, 0.70]]
|
|
204
|
+
if inside?(u, v, dress)
|
|
205
|
+
# eye ornaments
|
|
206
|
+
gy = v * 9
|
|
207
|
+
row = gy.floor
|
|
208
|
+
gx = u * 9 + (row.odd? ? 0.5 : 0.0)
|
|
209
|
+
lx = gx - gx.floor - 0.5
|
|
210
|
+
ly = gy - row - 0.5
|
|
211
|
+
e = (lx / 0.38)**2 + (ly / 0.2)**2
|
|
212
|
+
if e < 1
|
|
213
|
+
next [20, 20, 20] if e < 0.18
|
|
214
|
+
next e < 0.55 ? [240, 235, 215] : [120, 80, 30]
|
|
215
|
+
end
|
|
216
|
+
next jitter([225, 180, 60], u, v, 20, 40)
|
|
217
|
+
end
|
|
218
|
+
# background: gold with small squares and spirals
|
|
219
|
+
cx = (u * 16).floor
|
|
220
|
+
cy = (v * 16).floor
|
|
221
|
+
lx = u * 16 - cx
|
|
222
|
+
ly = v * 16 - cy
|
|
223
|
+
n = noise(cx, cy)
|
|
224
|
+
if n > 0.7 && lx.between?(0.2, 0.8) && ly.between?(0.2, 0.8)
|
|
225
|
+
next n > 0.85 ? [150, 110, 40] : [210, 205, 180]
|
|
226
|
+
end
|
|
227
|
+
next [170, 120, 40] if u < 0.3 && v < 0.6 && ModernAncient.spiral(u - 0.15, v - 0.35, 0.04) < 0.2
|
|
228
|
+
jitter([200, 160, 55], u, v, 22, 50)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Henri Rousseau, The Sleeping Gypsy
|
|
232
|
+
piece :the_sleeping_gypsy, "The Sleeping Gypsy", "Henri Rousseau", "1897", aspect: 1.55 do |u, v|
|
|
233
|
+
sx = 1.55
|
|
234
|
+
# the lion, sniffing from behind
|
|
235
|
+
if ellipse(u, v, 0.53, 0.50, 0.035, 0.065) < 1
|
|
236
|
+
next [30, 25, 20] if ellipse(u, v, 0.525, 0.475, 0.008, 0.012) < 1
|
|
237
|
+
next [210, 170, 110]
|
|
238
|
+
end
|
|
239
|
+
next [125, 80, 45] if ellipse(u, v, 0.56, 0.47, 0.065, 0.14) < 1 && u < 0.595 # mane
|
|
240
|
+
lion = [195, 155, 95]
|
|
241
|
+
next lion if ellipse(u, v, 0.71, 0.50, 0.15, 0.085) < 1
|
|
242
|
+
next lion if v.between?(0.52, 0.70) && [0.60, 0.645, 0.78, 0.825].any? { |lx| (u - lx).abs < 0.014 }
|
|
243
|
+
next lion if ModernAncient.seg(u, v, 0.85, 0.48, 0.90, 0.33, sx) < 0.012
|
|
244
|
+
next [110, 70, 40] if dist(u, v, 0.90, 0.32, sx) < 0.02
|
|
245
|
+
# the sleeping woman in a striped robe
|
|
246
|
+
if ellipse(u, v, 0.17, 0.75, 0.04, 0.05) < 1
|
|
247
|
+
next u < 0.17 ? [25, 20, 20] : [90, 55, 40]
|
|
248
|
+
end
|
|
249
|
+
if ellipse(u, v, 0.44, 0.785, 0.25, 0.06) < 1
|
|
250
|
+
s = (u * 30 + v * 20).floor % 5
|
|
251
|
+
next [[200, 60, 50], [235, 150, 60], [230, 210, 120], [70, 100, 160], [235, 230, 210]][s]
|
|
252
|
+
end
|
|
253
|
+
next [80, 50, 35] if ellipse(u, v, 0.70, 0.81, 0.03, 0.015) < 1 # feet
|
|
254
|
+
# mandolin and jug
|
|
255
|
+
next [60, 35, 20] if ellipse(u, v, 0.48, 0.91, 0.022, 0.02) < 1
|
|
256
|
+
next [190, 120, 50] if ellipse(u, v, 0.48, 0.91, 0.065, 0.045) < 1
|
|
257
|
+
next [120, 70, 30] if u.between?(0.34, 0.42) && (v - 0.91).abs < 0.01
|
|
258
|
+
next [175, 95, 55] if ellipse(u, v, 0.84, 0.82, 0.025, 0.05) < 1 || ellipse(u, v, 0.84, 0.765, 0.012, 0.02) < 1
|
|
259
|
+
# moon
|
|
260
|
+
next [235, 232, 205] if dist(u, v, 0.86, 0.13, sx) < 0.055
|
|
261
|
+
# sand
|
|
262
|
+
next jitter(mix([205, 180, 125], [175, 150, 100], (v - 0.6) * 2.5), u, v, 10, 25) if v > 0.585
|
|
263
|
+
# river and far bank hills
|
|
264
|
+
next mix([200, 205, 195], [160, 175, 175], Math.sin(u * 40) * 0.5 + 0.5) if v > 0.555
|
|
265
|
+
hill = 0.47 + 0.04 * Math.sin(u * 7 + 1) + 0.02 * Math.sin(u * 17)
|
|
266
|
+
next mix([170, 165, 150], [130, 130, 125], (v - hill) * 10) if v > hill
|
|
267
|
+
mix([25, 50, 65], [90, 130, 130], smooth(0.0, 0.5, v))
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Henri Rousseau, Tiger in a Tropical Storm (Surprised!)
|
|
271
|
+
piece :surprised, "Tiger in a Tropical Storm (Surprised!)", "Henri Rousseau", "1891", aspect: 1.25 do |u, v|
|
|
272
|
+
sx = 1.25
|
|
273
|
+
col = nil
|
|
274
|
+
# the tiger crouching, facing left
|
|
275
|
+
head = ellipse(u, v, 0.36, 0.58, 0.065, 0.075)
|
|
276
|
+
body = ellipse(u, v, 0.52, 0.66, 0.17, 0.075)
|
|
277
|
+
leg = [[0.37, 0.64, 0.33, 0.78], [0.42, 0.66, 0.40, 0.78], [0.62, 0.66, 0.62, 0.77], [0.66, 0.66, 0.69, 0.76]]
|
|
278
|
+
.any? { |a, b, c, d| ModernAncient.seg(u, v, a, b, c, d, sx) < 0.022 }
|
|
279
|
+
tail = ModernAncient.seg(u, v, 0.68, 0.64, 0.80, 0.56, sx) < 0.013 || ModernAncient.seg(u, v, 0.80, 0.56, 0.84, 0.47, sx) < 0.012
|
|
280
|
+
if head < 1
|
|
281
|
+
col = if ellipse(u, v, 0.34, 0.555, 0.012, 0.01) < 1 || ellipse(u, v, 0.385, 0.555, 0.012, 0.01) < 1
|
|
282
|
+
[240, 230, 160]
|
|
283
|
+
elsif ellipse(u, v, 0.34, 0.625, 0.03, 0.02) < 1
|
|
284
|
+
[200, 60, 60]
|
|
285
|
+
elsif ellipse(u, v, 0.33, 0.61, 0.045, 0.035) < 1
|
|
286
|
+
[245, 235, 215]
|
|
287
|
+
else
|
|
288
|
+
Math.sin(v * 120) > 0.6 ? [30, 20, 15] : [230, 135, 45]
|
|
289
|
+
end
|
|
290
|
+
elsif body < 1 || leg || tail
|
|
291
|
+
col = Math.sin(u * 90 + Math.sin(v * 25) * 2) > 0.45 ? [30, 20, 15] : mix([235, 145, 50], [245, 220, 170], (v - 0.64) * 8)
|
|
292
|
+
end
|
|
293
|
+
unless col
|
|
294
|
+
lx = 0.80 + 0.06 * ModernAncient.tri(v * 6)
|
|
295
|
+
if v < 0.4 && (u - lx).abs < 0.008
|
|
296
|
+
col = [250, 250, 220] # lightning
|
|
297
|
+
else
|
|
298
|
+
# layered leaves: the topmost leaf of the 3x3 neighbouring cells wins
|
|
299
|
+
gx = u * 8
|
|
300
|
+
gy = v * 7
|
|
301
|
+
best = -1.0
|
|
302
|
+
(-1..1).each do |di|
|
|
303
|
+
(-1..1).each do |dj|
|
|
304
|
+
cx = gx.floor + di
|
|
305
|
+
cy = gy.floor + dj
|
|
306
|
+
n = noise(cx, cy)
|
|
307
|
+
next if n < best
|
|
308
|
+
ox = cx + 0.5 + (noise(cx + 9, cy) - 0.5) * 0.6
|
|
309
|
+
oy = cy + 0.5 + (noise(cx, cy + 9) - 0.5) * 0.6
|
|
310
|
+
a = noise(cx + 3, cy + 5) * Math::PI
|
|
311
|
+
px = gx - ox
|
|
312
|
+
py = gy - oy
|
|
313
|
+
lx2 = px * Math.cos(a) + py * Math.sin(a)
|
|
314
|
+
ly2 = -px * Math.sin(a) + py * Math.cos(a)
|
|
315
|
+
e = (lx2 / 0.95)**2 + (ly2 / 0.32)**2
|
|
316
|
+
next if e >= 1
|
|
317
|
+
best = n
|
|
318
|
+
greens = [[35, 85, 40], [80, 140, 55], [25, 60, 35], [120, 160, 60], [55, 110, 75], [150, 135, 55], [95, 125, 45]]
|
|
319
|
+
g = greens[(noise(cx + 1, cy + 2) * greens.size).floor]
|
|
320
|
+
col = ly2.abs < 0.04 || e > 0.75 ? Color.scale(g, 0.7) : g
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
col ||= mix([140, 150, 130], [20, 40, 25], v * 2.5)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
# slanting rain
|
|
327
|
+
r = u * 22 + v * 9
|
|
328
|
+
(r - r.floor) < 0.07 ? mix(col, [210, 220, 220], 0.45) : col
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Malevich, Black Square
|
|
332
|
+
piece :black_square, "Black Square", "Kazimir Malevich", "1915", aspect: 1.0 do |u, v|
|
|
333
|
+
if u.between?(0.17, 0.83) && v.between?(0.17 + (u - 0.17) * 0.01, 0.83)
|
|
334
|
+
next [120, 105, 80] if (vnoise(u, v, 9) - 0.5).abs < 0.018
|
|
335
|
+
next [80, 70, 55] if (vnoise(u + 3, v, 17) - 0.5).abs < 0.02
|
|
336
|
+
next jitter([22, 20, 18], u, v, 6, 20)
|
|
337
|
+
end
|
|
338
|
+
jitter(mix([238, 232, 215], [220, 210, 185], vnoise(u, v, 4)), u, v, 6, 30)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Kandinsky, Composition VII
|
|
342
|
+
piece :composition_vii, "Composition VII", "Wassily Kandinsky", "1913", aspect: 1.5 do |u, v|
|
|
343
|
+
sx = 1.5
|
|
344
|
+
ink = [20, 15, 20]
|
|
345
|
+
# black lines whipping across
|
|
346
|
+
waves = [[0.30, 0.12, 9, 0.0], [0.62, 0.08, 7, 1.5], [0.80, 0.10, 11, 3.0], [0.20, 0.06, 15, 2.0],
|
|
347
|
+
[0.45, 0.15, 5, 4.0], [0.92, 0.04, 19, 1.0]]
|
|
348
|
+
next ink if waves.any? { |a, b, f, ph| (v - (a + b * Math.sin(u * f + ph))).abs < 0.007 }
|
|
349
|
+
next ink if ModernAncient.seg(u, v, 0.55, 0.15, 0.80, 0.90, sx) < 0.006
|
|
350
|
+
next ink if ModernAncient.seg(u, v, 0.10, 0.85, 0.50, 0.05, sx) < 0.006
|
|
351
|
+
# the dark vortex left of center
|
|
352
|
+
d = dist(u, v, 0.40, 0.48, sx)
|
|
353
|
+
if d < 0.17
|
|
354
|
+
next [10, 10, 15] if d < 0.04
|
|
355
|
+
a = Math.atan2(v - 0.48, (u - 0.40) * sx)
|
|
356
|
+
next Math.sin(d * 70 - a * 3) > 0.2 ? [25, 20, 30] : mix([70, 40, 110], [180, 60, 50], vnoise(u, v, 12))
|
|
357
|
+
end
|
|
358
|
+
# colour blobs
|
|
359
|
+
wob = (vnoise(u, v, 10) - 0.5) * 0.8 - 0.25
|
|
360
|
+
blob = [[0.66, 0.28, 0.12, 0.16, [40, 70, 175]], [0.22, 0.72, 0.10, 0.14, [205, 40, 40]],
|
|
361
|
+
[0.58, 0.70, 0.13, 0.10, [245, 205, 50]], [0.84, 0.66, 0.08, 0.13, [55, 150, 90]],
|
|
362
|
+
[0.18, 0.24, 0.08, 0.11, [130, 60, 145]], [0.88, 0.22, 0.06, 0.09, [235, 125, 40]],
|
|
363
|
+
[0.45, 0.16, 0.06, 0.07, [235, 150, 175]], [0.06, 0.50, 0.05, 0.12, [60, 120, 190]],
|
|
364
|
+
[0.72, 0.90, 0.10, 0.06, [110, 60, 40]]].find { |cx, cy, rx, ry, _| ellipse(u, v, cx, cy, rx, ry) + wob < 1 }
|
|
365
|
+
next blob[4] if blob
|
|
366
|
+
ramp(vnoise(u, v, 4) * 0.6 + vnoise(u + 2, v, 11) * 0.4, [240, 220, 170], [150, 190, 215], [235, 170, 140], [180, 215, 150], [225, 200, 215])
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Kandinsky, Several Circles
|
|
370
|
+
piece :several_circles, "Several Circles", "Wassily Kandinsky", "1926", aspect: 1.0 do |u, v|
|
|
371
|
+
col = mix([35, 40, 70], [8, 8, 15], dist(u, v, 0.55, 0.55) * 1.8)
|
|
372
|
+
halo = (dist(u, v, 0.36, 0.36) - 0.25).abs
|
|
373
|
+
col = mix(col, [70, 110, 210], 0.5 * (1 - halo / 0.05)) if halo < 0.05
|
|
374
|
+
[[0.36, 0.36, 0.22, [25, 30, 90], 0.95], [0.62, 0.60, 0.13, [205, 60, 60], 0.75],
|
|
375
|
+
[0.54, 0.46, 0.08, [245, 205, 60], 0.75], [0.72, 0.30, 0.065, [90, 185, 140], 0.75],
|
|
376
|
+
[0.30, 0.72, 0.09, [175, 80, 180], 0.7], [0.50, 0.78, 0.05, [240, 140, 60], 0.8],
|
|
377
|
+
[0.78, 0.76, 0.075, [60, 150, 225], 0.7], [0.46, 0.28, 0.04, [235, 235, 220], 0.8],
|
|
378
|
+
[0.18, 0.52, 0.035, [225, 90, 50], 0.8], [0.86, 0.50, 0.04, [205, 225, 90], 0.75],
|
|
379
|
+
[0.64, 0.18, 0.03, [235, 100, 150], 0.8], [0.40, 0.56, 0.05, [60, 175, 190], 0.6],
|
|
380
|
+
[0.70, 0.62, 0.03, [250, 250, 240], 0.8], [0.25, 0.25, 0.06, [60, 60, 150], 0.5]].each do |cx, cy, r, c, a|
|
|
381
|
+
d = dist(u, v, cx, cy)
|
|
382
|
+
col = mix(col, mix(c, Color.scale(c, 1.25), d / r), a) if d < r
|
|
383
|
+
end
|
|
384
|
+
col
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Matisse, Dance
|
|
388
|
+
piece :the_dance, "Dance", "Henri Matisse", "1910", aspect: 1.5 do |u, v|
|
|
389
|
+
sx = 1.5
|
|
390
|
+
# five dancers in a ring: [x, y (hip), lean, size]
|
|
391
|
+
figs = [[0.13, 0.48, -0.10, 1.15], [0.36, 0.30, 0.05, 0.95], [0.62, 0.30, -0.04, 0.9],
|
|
392
|
+
[0.86, 0.45, 0.08, 1.05], [0.48, 0.68, -0.12, 1.1]]
|
|
393
|
+
shoulders = figs.map { |x, y, l, s| [x + l * 0.6, y - 0.22 * s] }
|
|
394
|
+
hit = figs.each_with_index.any? do |(x, y, l, s), i|
|
|
395
|
+
hx, hy = shoulders[i]
|
|
396
|
+
dist(u, v, hx + l * 0.25, hy - 0.07 * s, sx) < 0.055 * s ||
|
|
397
|
+
ModernAncient.seg(u, v, x, y, hx, hy + 0.03, sx) < 0.06 * s ||
|
|
398
|
+
ModernAncient.seg(u, v, x, y, x - 0.07 * s, y + 0.27 * s, sx) < 0.032 * s ||
|
|
399
|
+
ModernAncient.seg(u, v, x, y, x + 0.08 * s, y + 0.24 * s, sx) < 0.032 * s
|
|
400
|
+
end
|
|
401
|
+
# linked arms around the ring, except the famous gap between the front pair
|
|
402
|
+
hit ||= [[0, 1], [1, 2], [2, 3], [3, 4]].any? do |a, b|
|
|
403
|
+
ax, ay = shoulders[a]
|
|
404
|
+
bx, by = shoulders[b]
|
|
405
|
+
mx = (ax + bx) / 2
|
|
406
|
+
my = (ay + by) / 2 - 0.05
|
|
407
|
+
ModernAncient.seg(u, v, ax, ay, mx, my, sx) < 0.024 || ModernAncient.seg(u, v, mx, my, bx, by, sx) < 0.024
|
|
408
|
+
end
|
|
409
|
+
fx, fy = shoulders[4]
|
|
410
|
+
gx, gy = shoulders[0]
|
|
411
|
+
hit ||= ModernAncient.seg(u, v, fx, fy, fx - 0.13, fy + 0.04, sx) < 0.024 || ModernAncient.seg(u, v, gx, gy, gx + 0.08, gy + 0.20, sx) < 0.024
|
|
412
|
+
next jitter([215, 85, 50], u, v, 10, 20) if hit
|
|
413
|
+
hill = 0.70 - 0.13 * Math.sin(Math::PI * (u * 0.9 + 0.08))
|
|
414
|
+
next jitter([55, 145, 75], u, v, 10, 12) if v > hill
|
|
415
|
+
jitter([35, 65, 155], u, v, 10, 12)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Munch, Madonna
|
|
419
|
+
piece :madonna_munch, "Madonna", "Edvard Munch", "1894", aspect: 0.76 do |u, v|
|
|
420
|
+
skin = [236, 214, 190]
|
|
421
|
+
hair = [18, 14, 16]
|
|
422
|
+
# face, tilted back, eyes closed
|
|
423
|
+
fe = ellipse(u, v, 0.50, 0.31, 0.12, 0.10)
|
|
424
|
+
if fe < 1
|
|
425
|
+
next [60, 40, 40] if ((u - 0.50).abs - 0.05).abs < 0.025 && (v - 0.30).abs < 0.008
|
|
426
|
+
next [170, 60, 60] if ellipse(u, v, 0.50, 0.37, 0.02, 0.008) < 1
|
|
427
|
+
next mix(skin, [200, 170, 150], fe * 0.8)
|
|
428
|
+
end
|
|
429
|
+
# red beret / halo
|
|
430
|
+
next [205, 40, 40] if ellipse(u, v, 0.50, 0.215, 0.18, 0.05) < 1 && v < 0.235
|
|
431
|
+
# neck and shoulders, cut off by the swirl below
|
|
432
|
+
if v < 0.64 + 0.03 * Math.sin(u * 14)
|
|
433
|
+
# long black hair flowing down both sides
|
|
434
|
+
next hair if (u - (0.32 + 0.04 * Math.sin(v * 15))).abs < 0.07 && v > 0.24
|
|
435
|
+
next hair if (u - (0.68 + 0.04 * Math.sin(v * 15 + 2))).abs < 0.06 && v > 0.24
|
|
436
|
+
next hair if ellipse(u, v, 0.50, 0.28, 0.17, 0.10) < 1 && v < 0.3
|
|
437
|
+
# raised arm behind the head
|
|
438
|
+
next mix(skin, [205, 180, 160], 0.4) if ModernAncient.seg(u, v, 0.22, 0.60, 0.20, 0.30, 0.76) < 0.045
|
|
439
|
+
next skin if (u - 0.50).abs < 0.06 && v.between?(0.38, 0.48)
|
|
440
|
+
next mix(skin, [210, 185, 165], (v - 0.45) * 3) if ellipse(u, v, 0.50, 0.62, 0.28, 0.17) < 1
|
|
441
|
+
end
|
|
442
|
+
# swirling dark background
|
|
443
|
+
d = Math.sqrt(ellipse(u, v, 0.50, 0.42, 0.45, 0.55)) + vnoise(u, v, 5) * 0.35
|
|
444
|
+
s = Math.sin(d * 16 + Math.sin(Math.atan2(v - 0.45, u - 0.5) * 3) * 0.8)
|
|
445
|
+
ramp(s * 0.5 + 0.5, [30, 25, 35], [25, 12, 18], [75, 18, 22], [130, 30, 30])
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Modigliani, Jeanne Hébuterne
|
|
449
|
+
piece :portrait_of_jeanne_hebuterne, "Jeanne Hébuterne", "Amedeo Modigliani", "1918", aspect: 0.65 do |u, v|
|
|
450
|
+
sx = 0.65
|
|
451
|
+
th = 0.16
|
|
452
|
+
du = (u - 0.52) * sx
|
|
453
|
+
dv = v - 0.27
|
|
454
|
+
x = du * Math.cos(th) + dv * Math.sin(th)
|
|
455
|
+
y = -du * Math.sin(th) + dv * Math.cos(th)
|
|
456
|
+
skin = [236, 196, 160]
|
|
457
|
+
hair = [135, 60, 35]
|
|
458
|
+
if (x / 0.095)**2 + (y / 0.15)**2 < 1 && y > -0.07
|
|
459
|
+
next [85, 115, 125] if ((x.abs - 0.038) / 0.026)**2 + ((y + 0.01) / 0.011)**2 < 1 # almond eyes
|
|
460
|
+
next [195, 70, 60] if (x / 0.022)**2 + ((y - 0.095) / 0.008)**2 < 1 # mouth
|
|
461
|
+
next mix(skin, [200, 150, 115], 0.6) if x.between?(-0.004, 0.01) && y.between?(0.0, 0.06) # nose
|
|
462
|
+
next mix(skin, [215, 160, 125], smooth(0.0, 0.1, -x) * 0.5)
|
|
463
|
+
end
|
|
464
|
+
# reddish-brown hair
|
|
465
|
+
next jitter(hair, u, v, 15, 25) if (x / 0.12)**2 + ((y + 0.02) / 0.17)**2 < 1 && y < 0.08
|
|
466
|
+
next jitter(hair, u, v, 15, 25) if ((x - 0.085) / 0.04)**2 + ((y - 0.12) / 0.10)**2 < 1
|
|
467
|
+
# long neck, leaning with the head
|
|
468
|
+
nc = 0.50 - (v - 0.40) * 0.12
|
|
469
|
+
if (u - nc).abs < 0.065 && v.between?(0.38, 0.66)
|
|
470
|
+
next mix(skin, [210, 160, 125], ((u - nc) / 0.065 + 1) * 0.4)
|
|
471
|
+
end
|
|
472
|
+
# dark dress with sloping shoulders
|
|
473
|
+
shoulder = 0.60 + 0.5 * (u - 0.49).abs**1.3
|
|
474
|
+
if v > shoulder
|
|
475
|
+
next [225, 220, 205] if v < shoulder + 0.03 && (u - 0.49).abs < 0.2 && v < 0.72
|
|
476
|
+
next jitter([40, 40, 55], u, v, 10, 15)
|
|
477
|
+
end
|
|
478
|
+
# warm ochre background, darker door at the left
|
|
479
|
+
next jitter([140, 85, 50], u, v, 12, 15) if u < 0.18
|
|
480
|
+
jitter(mix([215, 150, 75], [190, 120, 60], v), u, v, 14, 12)
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Paul Klee, Senecio
|
|
484
|
+
piece :senecio, "Senecio", "Paul Klee", "1922", aspect: 0.94 do |u, v|
|
|
485
|
+
sx = 0.94
|
|
486
|
+
d = dist(u, v, 0.50, 0.46, sx)
|
|
487
|
+
if d < 0.40
|
|
488
|
+
# eyes: a red square and a red dot
|
|
489
|
+
next [210, 40, 40] if (u - 0.37).abs < 0.03 && (v - 0.42).abs < 0.03
|
|
490
|
+
next [240, 230, 210] if (u - 0.37).abs < 0.06 && (v - 0.42).abs < 0.055
|
|
491
|
+
next [200, 40, 40] if dist(u, v, 0.63, 0.43, sx) < 0.03
|
|
492
|
+
next [245, 235, 215] if dist(u, v, 0.63, 0.43, sx) < 0.055
|
|
493
|
+
# raised eyebrow arc and the nose / mouth lines
|
|
494
|
+
next [140, 70, 40] if (dist(u, v, 0.43, 0.42, sx) - 0.13).abs < 0.01 && v < 0.38
|
|
495
|
+
next [150, 80, 50] if (u - 0.50).abs < 0.008 && v.between?(0.36, 0.58)
|
|
496
|
+
next [150, 80, 50] if (v - 0.62).abs < 0.008 && (u - 0.50).abs < 0.06
|
|
497
|
+
next [190, 100, 60] if (d - 0.39).abs < 0.012
|
|
498
|
+
# flat geometric blocks
|
|
499
|
+
cx = ((u - 0.10) / 0.2).floor
|
|
500
|
+
cy = ((v - 0.06) / 0.16).floor
|
|
501
|
+
pal = [[240, 170, 80], [245, 200, 120], [235, 140, 90], [245, 210, 170], [230, 120, 60], [240, 185, 150]]
|
|
502
|
+
c = pal[(noise(cx + 4, cy + 2) * pal.size).floor]
|
|
503
|
+
c = mix(c, [250, 235, 210], 0.35) if u < 0.5 && v < 0.55
|
|
504
|
+
next jitter(c, u, v, 8, 20)
|
|
505
|
+
end
|
|
506
|
+
# neck and shoulders at the bottom
|
|
507
|
+
next [235, 175, 120] if v > 0.84 && (u - 0.5).abs < 0.12
|
|
508
|
+
next [215, 110, 60] if v > 0.92
|
|
509
|
+
jitter(mix([215, 120, 55], [195, 95, 45], v), u, v, 10, 20)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
# Mondrian, Composition with Red, Blue and Yellow
|
|
513
|
+
piece :composition_with_red_blue_yellow, "Composition with Red, Blue and Yellow", "Piet Mondrian", "1930", aspect: 1.0 do |u, v|
|
|
514
|
+
w = 0.022
|
|
515
|
+
line = (u - 0.30).abs < w || (v - 0.70).abs < w ||
|
|
516
|
+
(u < 0.30 && (v - 0.36).abs < w) ||
|
|
517
|
+
(v > 0.70 && (u - 0.92).abs < w * 0.8) ||
|
|
518
|
+
(u > 0.92 && (v - 0.86).abs < w * 0.8)
|
|
519
|
+
next [20, 20, 25] if line
|
|
520
|
+
next [220, 35, 30] if u > 0.30 && v < 0.70
|
|
521
|
+
next [30, 50, 150] if u < 0.30 && v > 0.70
|
|
522
|
+
next [245, 215, 50] if u > 0.92 && v > 0.86
|
|
523
|
+
[240, 238, 230]
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# Hilma af Klint, The Ten Largest, No. 7, Adulthood
|
|
527
|
+
piece :the_ten_largest_no7, "The Ten Largest, No. 7, Adulthood", "Hilma af Klint", "1907", aspect: 0.75 do |u, v|
|
|
528
|
+
sx = 0.75
|
|
529
|
+
# big yellow spiral
|
|
530
|
+
dx = (u - 0.40) * sx
|
|
531
|
+
dy = v - 0.30
|
|
532
|
+
next [250, 225, 110] if Math.hypot(dx, dy) < 0.20 && ModernAncient.spiral(dx, dy, 0.045) < 0.35
|
|
533
|
+
# rosettes: [cx, cy, radius, petals, colour, centre colour]
|
|
534
|
+
petal = nil
|
|
535
|
+
[[0.68, 0.58, 0.16, 8, [165, 200, 230], [250, 230, 140]],
|
|
536
|
+
[0.28, 0.74, 0.13, 6, [250, 230, 140], [230, 110, 120]],
|
|
537
|
+
[0.78, 0.18, 0.09, 5, [240, 200, 215], [120, 150, 200]],
|
|
538
|
+
[0.22, 0.12, 0.06, 4, [165, 200, 230], [250, 250, 240]],
|
|
539
|
+
[0.60, 0.88, 0.07, 6, [240, 200, 215], [165, 200, 230]]].each do |cx, cy, rad, n, c, cc|
|
|
540
|
+
ex = (u - cx) * sx
|
|
541
|
+
ey = v - cy
|
|
542
|
+
rr = Math.hypot(ex, ey)
|
|
543
|
+
next if rr > rad
|
|
544
|
+
if rr < rad * 0.3
|
|
545
|
+
petal = cc
|
|
546
|
+
elsif rr < rad * (0.55 + 0.45 * Math.cos(Math.atan2(ey, ex) * n / 2.0).abs)
|
|
547
|
+
petal = c
|
|
548
|
+
end
|
|
549
|
+
break if petal
|
|
550
|
+
end
|
|
551
|
+
next petal if petal
|
|
552
|
+
# lettering-like curls
|
|
553
|
+
next [90, 60, 80] if u < 0.45 && (v - 0.47 - 0.015 * Math.sin(u * 70)).abs < 0.005
|
|
554
|
+
next [90, 60, 80] if v > 0.92 && u > 0.1 && ModernAncient.spiral((u * 30 % 1 - 0.5) * 0.04, v - 0.955, 0.008) < 0.25
|
|
555
|
+
# soft pale blue forms
|
|
556
|
+
next [190, 215, 230] if ellipse(u, v, 0.15, 0.50, 0.10, 0.07) + (vnoise(u, v, 12) - 0.5) * 0.5 < 1
|
|
557
|
+
jitter(mix([238, 150, 110], [230, 135, 100], v), u, v, 10, 15)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# Lascaux cave, the Hall of the Bulls
|
|
561
|
+
piece :lascaux_horses, "Lascaux Cave Paintings (Horses and Aurochs)", "unknown artists", "c. 17,000 BC", aspect: 1.8 do |u, v|
|
|
562
|
+
sx = 1.8
|
|
563
|
+
rock = ramp(vnoise(u, v, 5) * 0.7 + vnoise(u, v, 17) * 0.3, [140, 100, 65], [200, 165, 115], [230, 205, 160])
|
|
564
|
+
rock = Color.scale(rock, 0.65 + 0.35 * smooth(0.0, 0.35, v) - 0.25 * smooth(0.8, 1.0, v))
|
|
565
|
+
black = [30, 22, 18]
|
|
566
|
+
# the great aurochs
|
|
567
|
+
be = ellipse(u * sx, v, 0.55 * sx, 0.38, 0.30, 0.13)
|
|
568
|
+
next black if ellipse(u * sx, v, 0.33 * sx, 0.34, 0.06, 0.065) < 1
|
|
569
|
+
next black if be.between?(0.72, 1.0)
|
|
570
|
+
next black if be < 1 && noise((u * 50).floor, (v * 30).floor) > 0.9
|
|
571
|
+
next black if ModernAncient.seg(u, v, 0.32, 0.29, 0.29, 0.15, sx) < 0.012 || ModernAncient.seg(u, v, 0.35, 0.29, 0.40, 0.16, sx) < 0.012
|
|
572
|
+
legs = [[0.42, 0.48, 0.41, 0.66], [0.47, 0.50, 0.48, 0.66], [0.64, 0.48, 0.66, 0.64], [0.68, 0.46, 0.72, 0.62]]
|
|
573
|
+
next black if legs.any? { |a, b, c, d| ModernAncient.seg(u, v, a, b, c, d, sx) < 0.016 }
|
|
574
|
+
next black if ModernAncient.seg(u, v, 0.71, 0.36, 0.75, 0.55, sx) < 0.008
|
|
575
|
+
next mix(rock, [235, 215, 175], 0.3) if be < 1
|
|
576
|
+
# horses
|
|
577
|
+
horse = nil
|
|
578
|
+
[[0.15, 0.68, 1.0, [150, 60, 35]], [0.84, 0.72, 0.9, [90, 55, 30]], [0.55, 0.82, 0.6, [175, 80, 40]],
|
|
579
|
+
[0.90, 0.30, 0.6, [120, 70, 40]]].each do |x, y, s, c|
|
|
580
|
+
part = ModernAncient.horse(u, v, x, y, s, sx)
|
|
581
|
+
next unless part
|
|
582
|
+
horse = part == :body ? mix(c, [205, 150, 90], smooth(y, y + 0.06 * s, v)) : black
|
|
583
|
+
break
|
|
584
|
+
end
|
|
585
|
+
next horse if horse
|
|
586
|
+
# dots
|
|
587
|
+
dots = [[0.62, 0.15], [0.65, 0.15], [0.68, 0.15], [0.71, 0.15], [0.10, 0.30], [0.12, 0.33], [0.36, 0.86], [0.39, 0.86]]
|
|
588
|
+
next [60, 30, 20] if dots.any? { |x, y| dist(u, v, x, y, sx) < 0.018 }
|
|
589
|
+
# cracks
|
|
590
|
+
next Color.scale(rock, 0.7) if (vnoise(u + 5, v, 6) - 0.5).abs < 0.012
|
|
591
|
+
rock
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# Tomb of Nebamun, Pool in a Garden
|
|
595
|
+
piece :garden_of_nebamun, "Pool in a Garden (Tomb of Nebamun)", "unknown Egyptian artist", "c. 1350 BC", aspect: 0.86 do |u, v|
|
|
596
|
+
sx = 0.86
|
|
597
|
+
# the pool, seen from above
|
|
598
|
+
if u.between?(0.27, 0.73) && v.between?(0.30, 0.70)
|
|
599
|
+
next [60, 45, 35] if u < 0.285 || u > 0.715 || v < 0.315 || v > 0.685
|
|
600
|
+
# ducks and fish
|
|
601
|
+
if [[0.40, 0.42], [0.58, 0.58]].any? { |x, y| ellipse(u, v, x, y, 0.045, 0.025) < 1 }
|
|
602
|
+
next [235, 230, 215]
|
|
603
|
+
end
|
|
604
|
+
next [120, 80, 50] if [[0.40, 0.42], [0.58, 0.58]].any? { |x, y| ellipse(u, v, x - 0.05, y - 0.02, 0.015, 0.013) < 1 }
|
|
605
|
+
next [190, 150, 130] if [[0.58, 0.40], [0.40, 0.60], [0.50, 0.50]].any? { |x, y| ellipse(u, v, x, y, 0.04, 0.015) < 1 }
|
|
606
|
+
# lotus flowers along the edge
|
|
607
|
+
if (u < 0.33 || u > 0.67 || v < 0.36 || v > 0.64) && noise((u * 40).floor, (v * 40).floor) > 0.6
|
|
608
|
+
next noise((u * 40).floor + 1, (v * 40).floor) > 0.5 ? [240, 240, 230] : [70, 130, 120]
|
|
609
|
+
end
|
|
610
|
+
w = v * 22 + ModernAncient.tri(u * 25) * 0.4
|
|
611
|
+
next (w - w.floor) < 0.18 ? [30, 45, 80] : [70, 115, 170]
|
|
612
|
+
end
|
|
613
|
+
# rows of trees laid flat around the pool, pointing away from it
|
|
614
|
+
trees = []
|
|
615
|
+
5.times { |i| trees << [0.25 + i * 0.125, 0.27, 0.0, -1.0, i.odd?] }
|
|
616
|
+
5.times { |i| trees << [0.25 + i * 0.125, 0.73, 0.0, 1.0, i.even?] }
|
|
617
|
+
3.times { |i| trees << [0.25, 0.38 + i * 0.12, -1.0, 0.0, i.odd?] }
|
|
618
|
+
3.times { |i| trees << [0.75, 0.38 + i * 0.12, 1.0, 0.0, i.even?] }
|
|
619
|
+
tree = nil
|
|
620
|
+
trees.each do |bx, by, ddx, ddy, palm|
|
|
621
|
+
px = (u - bx) * sx
|
|
622
|
+
py = v - by
|
|
623
|
+
tree = ModernAncient.nebamun_tree(u, v, px * ddx + py * ddy, px * ddy - py * ddx, palm)
|
|
624
|
+
break if tree
|
|
625
|
+
end
|
|
626
|
+
next tree if tree
|
|
627
|
+
jitter([226, 205, 165], u, v, 10, 20)
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
# The Bayeux Tapestry, Harold's oath to William
|
|
631
|
+
piece :bayeux_tapestry, "Bayeux Tapestry (Harold's Oath)", "unknown embroiderers", "c. 1070", aspect: 2.6 do |u, v|
|
|
632
|
+
sx = 2.6
|
|
633
|
+
terra = [175, 90, 60]
|
|
634
|
+
olive = [120, 125, 65]
|
|
635
|
+
blue = [85, 105, 130]
|
|
636
|
+
linen = jitter([236, 224, 192], u, v, 8, 40)
|
|
637
|
+
# border lines and the borders with small beasts
|
|
638
|
+
next [120, 90, 70] if (v - 0.17).abs < 0.008 || (v - 0.83).abs < 0.008
|
|
639
|
+
if v < 0.17 || v > 0.83
|
|
640
|
+
bv = v < 0.17 ? v / 0.17 : (v - 0.83) / 0.17
|
|
641
|
+
k = u * 30 - bv * 0.6
|
|
642
|
+
cell = k.floor
|
|
643
|
+
lx = k - cell
|
|
644
|
+
next [terra, olive, blue][cell % 3] if lx < 0.12
|
|
645
|
+
next [olive, terra][(cell / 2) % 2] if cell.even? && ellipse(lx, bv, 0.55, 0.5, 0.22, 0.17) < 1
|
|
646
|
+
next linen
|
|
647
|
+
end
|
|
648
|
+
# Latin inscription along the top
|
|
649
|
+
if v.between?(0.21, 0.29) && u.between?(0.08, 0.92)
|
|
650
|
+
lx = u * 70
|
|
651
|
+
li = lx.floor
|
|
652
|
+
f = lx - li
|
|
653
|
+
n = noise(li, 3)
|
|
654
|
+
next [55, 65, 95] if n > 0.15 && (f < 0.3 || (n > 0.6 && (v - 0.25).abs < 0.012 && f < 0.7))
|
|
655
|
+
next linen
|
|
656
|
+
end
|
|
657
|
+
# William on his throne, Harold swearing, two witnesses
|
|
658
|
+
fig = nil
|
|
659
|
+
[[0.14, blue, :seated], [0.21, olive, :stand], [0.44, terra, :oath], [0.60, olive, :stand],
|
|
660
|
+
[0.655, blue, :stand], [0.71, terra, :stand]].each do |x, c, kind|
|
|
661
|
+
fig = ModernAncient.bayeux_figure(u, v, x, c, kind, sx)
|
|
662
|
+
break if fig
|
|
663
|
+
end
|
|
664
|
+
next fig if fig
|
|
665
|
+
# two reliquaries Harold swears on
|
|
666
|
+
[0.375, 0.505].each do |x|
|
|
667
|
+
fig = [200, 160, 80] if (u - x).abs < 0.02 && v.between?(0.53, 0.62)
|
|
668
|
+
fig = blue if (u - x).abs < 0.005 && v.between?(0.62, 0.79)
|
|
669
|
+
end
|
|
670
|
+
next fig if fig
|
|
671
|
+
# a building at the left and a horse at the right
|
|
672
|
+
next [terra, olive][(u * 80).floor % 2] if u.between?(0.02, 0.06) && v.between?(0.32, 0.80)
|
|
673
|
+
next terra if u < 0.08 && v.between?(0.30, 0.34)
|
|
674
|
+
next olive if ellipse(u, v, 0.86, 0.55, 0.06, 0.09) < 1
|
|
675
|
+
next olive if ModernAncient.seg(u, v, 0.81, 0.52, 0.78, 0.40, sx) < 0.02 || dist(u, v, 0.77, 0.40, sx) < 0.03
|
|
676
|
+
next olive if v.between?(0.60, 0.79) && [0.82, 0.84, 0.88, 0.90].any? { |x| (u - x).abs < 0.004 }
|
|
677
|
+
# a stylized tree between scenes
|
|
678
|
+
next olive if ModernAncient.seg(u, v, 0.28, 0.79, 0.28, 0.45, sx) < 0.006
|
|
679
|
+
next [terra, olive][(v * 40).floor % 2] if ellipse(u, v, 0.28, 0.42, 0.025, 0.08) < 1
|
|
680
|
+
linen
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
# Pompeii, the Alexander Mosaic
|
|
684
|
+
piece :alexander_mosaic, "Alexander Mosaic", "unknown, Pompeii", "c. 100 BC", aspect: 1.86 do |u, v|
|
|
685
|
+
sx = 1.86
|
|
686
|
+
# lost areas show bare plaster
|
|
687
|
+
lost = (u < 0.42 && vnoise(u, v, 5) + (0.42 - u) * 0.6 > 0.82) || (v > 0.86 && u < 0.3 && vnoise(u, v, 9) > 0.45)
|
|
688
|
+
next jitter([195, 182, 158], u, v, 6, 30) if lost && dist(u, v, 0.22, 0.34, sx) >= 0.08
|
|
689
|
+
# tesserae: quantize to tiles
|
|
690
|
+
nx = 80
|
|
691
|
+
ny = 43
|
|
692
|
+
iu = (u * nx).floor
|
|
693
|
+
iv = (v * ny).floor
|
|
694
|
+
shade = 0.88 + noise(iu, iv) * 0.2
|
|
695
|
+
shade *= 0.85 if u * nx - iu < 0.12 || v * ny - iv < 0.12
|
|
696
|
+
tu = (iu + 0.5) / nx
|
|
697
|
+
tv = (iv + 0.5) / ny
|
|
698
|
+
wheel = dist(tu, tv, 0.71, 0.66, sx)
|
|
699
|
+
col =
|
|
700
|
+
if dist(tu, tv, 0.22, 0.30, sx) < 0.045 then [205, 160, 120] # Alexander's head
|
|
701
|
+
elsif ellipse(tu, tv, 0.23, 0.42, 0.04, 0.09) < 1 then [130, 125, 115] # armour
|
|
702
|
+
elsif ellipse(tu, tv, 0.20, 0.60, 0.10, 0.11) < 1 || ModernAncient.seg(tu, tv, 0.26, 0.55, 0.31, 0.42, sx) < 0.04
|
|
703
|
+
[95, 60, 35] # Bucephalus
|
|
704
|
+
elsif dist(tu, tv, 0.70, 0.27, sx) < 0.04 then [195, 150, 110] # Darius
|
|
705
|
+
elsif ModernAncient.seg(tu, tv, 0.68, 0.33, 0.60, 0.28, sx) < 0.015 then [195, 150, 110] # his outstretched arm
|
|
706
|
+
elsif ellipse(tu, tv, 0.71, 0.38, 0.04, 0.07) < 1 then [120, 50, 70]
|
|
707
|
+
elsif dist(tu, tv, 0.80, 0.28, sx) < 0.035 then [190, 145, 110] # charioteer
|
|
708
|
+
elsif ellipse(tu, tv, 0.80, 0.38, 0.035, 0.07) < 1 then [130, 90, 60]
|
|
709
|
+
elsif (wheel - 0.09).abs < 0.015 || (wheel < 0.09 && Math.sin(Math.atan2(tv - 0.66, tu - 0.71) * 6).abs < 0.15)
|
|
710
|
+
[50, 35, 25] # chariot wheel
|
|
711
|
+
elsif tu.between?(0.62, 0.84) && tv.between?(0.44, 0.58) then [110, 70, 40] # chariot box
|
|
712
|
+
elsif ellipse(tu, tv, 0.90, 0.62, 0.09, 0.10) < 1 || ModernAncient.seg(tu, tv, 0.94, 0.58, 0.99, 0.45, sx) < 0.03
|
|
713
|
+
[35, 30, 28] # black horses
|
|
714
|
+
elsif ellipse(tu, tv, 0.48, 0.70, 0.09, 0.12) < 1 then [140, 90, 50] # Persian horse from behind
|
|
715
|
+
elsif ellipse(tu, tv, 0.52, 0.52, 0.03, 0.08) < 1 then [170, 130, 90]
|
|
716
|
+
elsif (tu - (0.33 + (0.6 - tv) * 0.05)).abs < 0.008 && tv.between?(0.08, 0.5) then [80, 70, 55] # bare tree
|
|
717
|
+
elsif ModernAncient.seg(tu, tv, 0.33, 0.20, 0.27, 0.08, sx) < 0.008 then [80, 70, 55]
|
|
718
|
+
elsif tv.between?(0.02, 0.55) && tu.between?(0.40, 0.90) && (((tu + (0.55 - tv) * 0.35) * 20) % 1) < 0.25
|
|
719
|
+
[45, 35, 28] # forest of spears
|
|
720
|
+
elsif tv > 0.80 && noise((tu * 20).floor, (tv * 12).floor) > 0.7 then [160, 40, 35] # fallen shields
|
|
721
|
+
elsif tv > 0.56 then [150, 110, 70] # ground
|
|
722
|
+
else [215, 190, 150]
|
|
723
|
+
end
|
|
724
|
+
Color.scale(col, shade)
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
# Ravenna, San Vitale: Emperor Justinian and His Retinue
|
|
728
|
+
piece :justinian_mosaic, "Emperor Justinian and His Retinue", "unknown, Ravenna", "c. 547", aspect: 1.7 do |u, v|
|
|
729
|
+
sx = 1.7
|
|
730
|
+
nx = 85
|
|
731
|
+
ny = 50
|
|
732
|
+
iu = (u * nx).floor
|
|
733
|
+
iv = (v * ny).floor
|
|
734
|
+
shade = 0.85 + noise(iu, iv) * 0.25
|
|
735
|
+
shade *= 0.88 if u * nx - iu < 0.12 || v * ny - iv < 0.12
|
|
736
|
+
# jewelled border at the top
|
|
737
|
+
next Color.scale((u * 30).floor.even? ? [40, 90, 80] : [120, 40, 60], shade) if v < 0.06
|
|
738
|
+
col = nil
|
|
739
|
+
# green shield with a chi-rho among the guards at the left
|
|
740
|
+
if ellipse(u, v, 0.13, 0.62, 0.05, 0.14) < 1
|
|
741
|
+
col = (u - 0.13).abs < 0.006 || (v - 0.62).abs < 0.008 ? [230, 200, 90] : [40, 110, 70]
|
|
742
|
+
end
|
|
743
|
+
9.times do |i|
|
|
744
|
+
break if col
|
|
745
|
+
x = 0.10 + i * 0.10
|
|
746
|
+
emperor = i == 4
|
|
747
|
+
hd = dist(u, v, x, 0.22, sx)
|
|
748
|
+
if emperor && (hd - 0.11).abs < 0.01
|
|
749
|
+
col = [120, 80, 30]
|
|
750
|
+
elsif emperor && hd < 0.11 && hd >= 0.065
|
|
751
|
+
col = [250, 215, 100]
|
|
752
|
+
elsif hd < 0.065
|
|
753
|
+
col = if v >= 0.195 then [225, 180, 150]
|
|
754
|
+
elsif emperor then [230, 200, 80]
|
|
755
|
+
else [60, 40, 30]
|
|
756
|
+
end
|
|
757
|
+
elsif (u - x).abs < 0.036 + (v - 0.30) * 0.04 && v.between?(0.30, 0.86)
|
|
758
|
+
cloak = [3, 5].include?(i) && u < x
|
|
759
|
+
col = if emperor
|
|
760
|
+
v > 0.36 && v < 0.5 && (u - x + 0.015).abs < 0.015 ? [230, 190, 80] : [110, 40, 95]
|
|
761
|
+
elsif cloak then [175, 130, 80]
|
|
762
|
+
elsif ((u - x).abs - 0.018).abs < 0.003 then [150, 110, 110]
|
|
763
|
+
elsif i > 6 && v < 0.5 then [210, 190, 110]
|
|
764
|
+
else [235, 230, 215]
|
|
765
|
+
end
|
|
766
|
+
elsif v.between?(0.86, 0.89) && ((u - x).abs - 0.015).abs < 0.01
|
|
767
|
+
col = [30, 25, 25]
|
|
768
|
+
end
|
|
769
|
+
end
|
|
770
|
+
col ||= v > 0.86 ? [60, 115, 60] : (noise(iu + 3, iv) > 0.8 ? [250, 220, 120] : [215, 170, 60])
|
|
771
|
+
Color.scale(col, shade)
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
end
|