MathArtist
/
operators.pyx
465 строк · 14.2 Кб
1
2# Copyright (c) 2018, Yaroslav Zotov, https://github.com/qiray/
3# All rights reserved.
4
5# This file is part of MathArtist.
6
7# MathArtist is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11
12# MathArtist is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16
17# You should have received a copy of the GNU General Public License
18# along with MathArtist. If not, see <https://www.gnu.org/licenses/>.
19
20################################################################################
21
22# This file uses code from Andrej Bauer's randomart project under
23# following conditions:
24
25# Copyright (c) 2010, Andrej Bauer, http://andrej.com/
26# All rights reserved.
27#
28# Redistribution and use in source and binary forms, with or without
29# modification, are permitted provided that the following conditions are met:
30#
31# * Redistributions of source code must retain the above copyright notice,
32# this list of conditions and the following disclaimer.
33#
34# * Redistributions in binary form must reproduce the above copyright
35# notice, this list of conditions and the following disclaimer in the
36# documentation and/or other materials provided with the distribution.
37#
38# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
42# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
44# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
45# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48
49# cython: language_level=3
50
51import random
52import math
53
54cimport libc.math as cmath
55
56from common import (average, well, tent, parse_color, sin_curve, abs_sin,
57color_binary, wave)
58from palettes import palettes
59
60# We next define classes that represent expression trees.
61
62# Each object that reprents and expression should have an eval(self, x, y) method
63# which computes the value of the expression at (x, y). The __init__ should
64# accept the objects representing its subexpressions. The class definition
65# should contain the arity attribute which tells how many subexpressions should
66# be passed to the __init__ constructor. Classes with arity == 0 are called
67# terminals, the others are called nonterminals.The __repr__ method is used to
68# print each object as a string. The mindepth attribute shows depth of
69# expression tree where it is allowed to use this object.
70
71# Some operators are adopted from https://github.com/vshymanskyy/randomart
72
73# Terminals:
74
75class VariableX():
76arity = 0
77mindepth = 4
78def __init__(self):
79pass
80def __repr__(self):
81return "x"
82def eval(self, x, y):
83return (x, x, x)
84
85class VariableY():
86arity = 0
87mindepth = 4
88def __init__(self):
89pass
90def __repr__(self):
91return "y"
92def eval(self, x, y):
93return (y, y, y)
94
95class Random():
96arity = 0
97mindepth = 4
98def __init__(self, r = None, g = None, b = None):
99if r and g and b: #for parsing
100self.c = (r, g, b)
101return
102self.c = (random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1))
103def __repr__(self):
104return 'Random(%g,%g,%g)' % self.c
105def eval(self, x, y):
106return self.c
107
108class Palette():
109arity = 0
110mindepth = 3
111palette = palettes[0]
112paletteIndex = 0
113def __init__(self, r = None, g = None, b = None):
114if r and g and b: #for parsing
115self.c = (r, g, b)
116return
117self.hex = Palette.palette[Palette.paletteIndex]
118Palette.paletteIndex += 1
119if Palette.paletteIndex >= len(Palette.palette):
120Palette.paletteIndex = 0
121self.c = tuple([x/128.0 - 1.0 for x in parse_color(self.hex)])
122def __repr__(self):
123return "Palette(%g, %g, %g)" % self.c
124def eval(self, x, y):
125return self.c
126
127@staticmethod
128def randomPalette(): #set random palette
129Palette.palette = random.choice(palettes)
130Palette.paletteIndex = 0
131
132class White():
133arity = 0
134mindepth = 4
135def __init__(self, r = None, g = None, b = None): #unused arguments for parsing
136self.c = (1, 1, 1)
137def __repr__(self):
138return 'White(%g, %g, %g)' % self.c
139def eval(self, x, y):
140return self.c
141
142class Chess():
143arity = 0
144mindepth = 5
145def __init__(self, wX=None, wY=None):
146if wX and wY: #for parsing
147self.wX = wX
148self.wY = wY
149return
150self.wX = random.uniform(0.1, 1.0)
151self.wY = random.uniform(0.1, 1.0)
152def __repr__(self):
153return "Chess(%g, %g)" % (self.wX, self.wY)
154def eval(self, x, y):
155isOdd = False
156isOdd ^= int(cmath.floor(x/self.wX)) & 1
157isOdd ^= int(cmath.floor(y/self.wY)) & 1
158return (-1, -1, -1) if isOdd else (1, 1, 1)
159
160class Fibonacci():
161arity = 0
162mindepth = 3
163fib_array = [0, 1]
164def __init__(self):
165pass
166def __repr__(self):
167return "Fibonacci()"
168def eval(self, x, y):
169result = (self.fibonacci(len(Fibonacci.fib_array) + 1)%255)/255 - 128
170return (result, result, result)
171def fibonacci(self, n):
172if n < 0:
173return Fibonacci.fib_array[0]
174elif n < len(Fibonacci.fib_array):
175return Fibonacci.fib_array[n]
176else:
177max_limit = 200 if n > 200 else n + 1
178for i in range(len(Fibonacci.fib_array), max_limit):
179Fibonacci.fib_array.append(Fibonacci.fib_array[i - 1] + Fibonacci.fib_array[i - 2])
180return Fibonacci.fib_array[max_limit - 1]
181
182# Nonterminals:
183
184class Well():
185arity = 1
186mindepth = 3
187def __init__(self, e):
188self.e = e
189self.e_func = self.e.eval
190def __repr__(self):
191return 'Well(%s)' % self.e
192def eval(self, x, y):
193(r, g, b) = self.e_func(x, y)
194return (well(r), well(g), well(b))
195
196class Tent():
197arity = 1
198mindepth = 3
199def __init__(self, e):
200self.e = e
201self.e_func = self.e.eval
202def __repr__(self):
203return 'Tent(%s)' % self.e
204def eval(self, x, y):
205(r, g, b) = self.e_func(x, y)
206return (tent(r), tent(g), tent(b))
207
208class Sin():
209arity = 1
210mindepth = 0
211def __init__(self, e, phase = None, freq = None):
212self.e = e
213self.e_func = self.e.eval
214if phase and freq: #for parsing
215self.phase = phase
216self.freq = freq
217return
218self.phase = random.uniform(0, math.pi)
219self.freq = random.uniform(1.0, 6.0)
220def __repr__(self):
221return 'Sin(%s, %g, %g)' % (self.e, self.phase, self.freq)
222def eval(self, x, y):
223(r1, g1, b1) = self.e_func(x, y)
224r2 = cmath.sin(self.phase + self.freq * r1)
225g2 = cmath.sin(self.phase + self.freq * g1)
226b2 = cmath.sin(self.phase + self.freq * b1)
227return (r2, g2, b2)
228
229class Not():
230arity = 1
231mindepth = 3
232def __init__(self, e):
233self.e = e
234self.e_func = self.e.eval
235def __repr__(self):
236return "Not(%s)" % self.e
237def eval(self, x, y):
238(r, g, b) = self.e_func(x, y)
239return (-r, -g, -b)
240
241class SinCurve():
242arity = 1
243mindepth = 0
244def __init__(self, e):
245self.e = e
246self.e_func = self.e.eval
247def __repr__(self):
248return 'SinCurve(%s)' % self.e
249def eval(self, x, y):
250(r, g, b) = self.e_func(x, y)
251return (sin_curve(r), sin_curve(g), sin_curve(b))
252
253class AbsSin():
254arity = 1
255mindepth = 3
256def __init__(self, e):
257self.e = e
258self.e_func = self.e.eval
259def __repr__(self):
260return 'AbsSin(%s)' % self.e
261def eval(self, x, y):
262(r, g, b) = self.e_func(x, y)
263return (abs_sin(r), abs_sin(g), abs_sin(b))
264
265class Atan():
266arity = 1
267mindepth = 0
268def __init__(self, e):
269self.e = e
270self.e_func = self.e.eval
271def __repr__(self):
272return 'Atan(%s)' % (self.e)
273def eval(self, x, y):
274(r, g, b) = self.e_func(x, y)
275return (cmath.atan(r)*2/cmath.pi, cmath.atan(g)*2/cmath.pi, cmath.atan(b)*2/cmath.pi)
276
277class Sum():
278arity = 2
279mindepth = 2
280def __init__(self, e1, e2):
281self.e1 = e1
282self.e2 = e2
283def __repr__(self):
284return 'Sum(%s, %s)' % (self.e1, self.e2)
285def eval(self, x, y):
286return average(self.e1.eval(x, y), self.e2.eval(x, y))
287
288class Product():
289arity = 2
290mindepth = 2
291def __init__(self, e1, e2):
292self.e1 = e1
293self.e2 = e2
294def __repr__(self):
295return 'Product(%s, %s)' % (self.e1, self.e2)
296def eval(self, x, y):
297(r1, g1, b1) = self.e1.eval(x, y)
298(r2, g2, b2) = self.e2.eval(x, y)
299r3 = r1 * r2
300g3 = g1 * g2
301b3 = b1 * b2
302return (r3, g3, b3)
303
304class Mod():
305arity = 2
306mindepth = 3
307def __init__(self, e1, e2):
308self.e1 = e1
309self.e2 = e2
310def __repr__(self):
311return 'Mod(%s, %s)' % (self.e1, self.e2)
312def eval(self, x, y):
313(r1, g1, b1) = self.e1.eval(x, y)
314(r2, g2, b2) = self.e2.eval(x, y)
315try:
316r3 = r1 % r2
317g3 = g1 % g2
318b3 = b1 % b2
319return (r3, g3, b3)
320except:
321return (0, 0, 0)
322
323class And():
324arity = 2
325mindepth = 0
326def __init__(self, e1, e2):
327self.e1 = e1
328self.e2 = e2
329def __repr__(self):
330return 'And(%s, %s)' % (self.e1, self.e2)
331def eval(self, x, y):
332return color_binary(self.e1.eval(x, y), self.e2.eval(x, y), lambda x1,x2 : x1 & x2)
333
334class Or():
335arity = 2
336mindepth = 0
337def __init__(self, e1, e2):
338self.e1 = e1
339self.e2 = e2
340def __repr__(self):
341return 'Or(%s, %s)' % (self.e1, self.e2)
342def eval(self, x, y):
343return color_binary(self.e1.eval(x, y), self.e2.eval(x, y), lambda x1,x2 : x1 | x2)
344
345class Xor():
346arity = 2
347mindepth = 0
348def __init__(self, e1, e2):
349self.e1 = e1
350self.e2 = e2
351def __repr__(self):
352return 'Xor(%s, %s)' % (self.e1, self.e2)
353def eval(self, x, y):
354return color_binary(self.e1.eval(x, y), self.e2.eval(x, y), lambda x1,x2 : x1 ^ x2)
355
356class Wave():
357arity = 2
358mindepth = 0
359def __init__(self, e1, e2):
360self.e1 = e1
361self.e2 = e2
362def __repr__(self):
363return 'Wave(%s, %s)' % (self.e1, self.e2)
364def eval(self, x, y):
365(r1, g1, b1) = self.e1.eval(x, y)
366(r2, g2, b2) = self.e2.eval(x, y)
367return (wave(r1, r2), wave(g1, g2), wave(b1, b2))
368
369class Level():
370arity = 3
371mindepth = 0
372def __init__(self, level, e1, e2, treshold = None):
373self.treshold = treshold if treshold else random.uniform(-1.0, 1.0) #for parsing
374self.level = level
375self.e1 = e1
376self.e2 = e2
377def __repr__(self):
378return 'Level(%s, %s, %s, %g)' % (self.level, self.e1, self.e2, self.treshold)
379def eval(self, x, y):
380(r1, g1, b1) = self.level.eval(x, y)
381(r2, g2, b2) = self.e1.eval(x, y)
382(r3, g3, b3) = self.e2.eval(x, y)
383r4 = r2 if r1 < self.treshold else r3
384g4 = g2 if g1 < self.treshold else g3
385b4 = b2 if b1 < self.treshold else b3
386return (r4, g4, b4)
387
388class Mix():
389arity = 3
390mindepth = 0
391def __init__(self, w, e1, e2):
392self.w = w
393self.e1 = e1
394self.e2 = e2
395self.w_func = self.w.eval
396self.e1_func = self.e1.eval
397self.e2_func = self.e2.eval
398def __repr__(self):
399return 'Mix(%s, %s, %s)' % (self.w, self.e1, self.e2)
400def eval(self, x, y):
401w = 0.5 * (self.w_func(x, y)[0] + 1.0)
402c1 = self.e1_func(x, y)
403c2 = self.e2_func(x, y)
404return average(c1, c2, w)
405
406class RGB():
407arity = 3
408mindepth = 4
409def __init__(self, e1, e2, e3):
410self.e1 = e1
411self.e2 = e2
412self.e3 = e3
413self.e1_func = self.e1.eval
414self.e2_func = self.e2.eval
415self.e3_func = self.e3.eval
416def __repr__(self):
417return 'RGB(%s, %s, %s)' % (self.e1, self.e2, self.e3)
418def eval(self, x, y):
419(r, _, _) = self.e1_func(x, y)
420(_, g, _) = self.e2_func(x, y)
421(_, _, b) = self.e3_func(x, y)
422return (r, g, b)
423
424class Closest():
425arity = 3
426mindepth = 3
427def __init__(self, target, e1, e2):
428self.target = target
429self.e1 = e1
430self.e2 = e2
431self.target_func = self.target.eval
432self.e1_func = self.e1.eval
433self.e2_func = self.e2.eval
434def __repr__(self):
435return 'Closest(%s, %s, %s)' % (self.target, self.e1, self.e2)
436def eval(self, x, y):
437(r1, g1, b1) = self.target_func(x, y)
438(r2, g2, b2) = self.e1_func(x, y)
439(r3, g3, b3) = self.e2_func(x, y)
440#distances between colors:
441d1 = math.sqrt((r2-r1)**2+(g2-g1)**2+(b2-b1)**2)
442d2 = math.sqrt((r3-r1)**2+(g3-g1)**2+(b3-b1)**2)
443
444return (r2, g2, b2) if d1 < d2 else (r3, g3, b3)
445
446class Far():
447arity = 3
448mindepth = 3
449def __init__(self, target, e1, e2):
450self.target = target
451self.e1 = e1
452self.e2 = e2
453self.target_func = self.target.eval
454self.e1_func = self.e1.eval
455self.e2_func = self.e2.eval
456def __repr__(self):
457return 'Far(%s, %s, %s)' % (self.target, self.e1, self.e2)
458def eval(self, x, y):
459(r1, g1, b1) = self.target_func(x, y)
460(r2, g2, b2) = self.e1_func(x, y)
461(r3, g3, b3) = self.e2_func(x, y)
462#distances between colors:
463d1 = math.sqrt((r2-r1)**2+(g2-g1)**2+(b2-b1)**2)
464d2 = math.sqrt((r3-r1)**2+(g3-g1)**2+(b3-b1)**2)
465
466return (r2, g2, b2) if d1 > d2 else (r3, g3, b3)
467