LBI/modules/fractale.py
2019-03-04 13:47:20 +01:00

222 lines
12 KiB
Python

# dummy module
import discord
import random
from concurrent.futures import ThreadPoolExecutor
import fractale.source.main
from PIL import Image
class MainClass():
def __init__(self, client, modules, owners, prefix):
self.client = client
self.modules = modules
self.owners = owners
self.prefix = prefix
self.events = ['on_message'] # events list
self.command = "%sfractale" % self.prefix # command prefix (can be empty to catch every single messages)
self.name = "Fractale"
self.description = "Module de génération de fractales"
self.interactive = True
self.color = 0x78ffc3
self.fractals = {
"von_koch_curve_flake": {"Type": "Figures", "Max": ((5000, 5000), 5000, 10, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
"Default": "(2500 2500) 2000 5 #81ff65 #191919 2",
"Indication": "(départ) rayon longueur iterations couleur_trait couleur_fond stroke",
"ParseData": "pfixxi"},
"von_koch_curve": {"Type": "Figures", "Max": ((5000, 5000), (5000, 5000), 10, 16777215, 16777215, 5000), "Min": ((0, 0), (0, 0), 1, 0, 0, 0),
"Default": "(0 2500) (5000 2500) 5 #81ff65 #191919 2",
"Indication": "(départ) (arrivée) iterations couleur_trait couleur_fond stroke", "ParseData": "ppixxi"},
"blanc_manger": {"Type": "Figures", "Max": ((5000, 5000), (5000, 5000), 10, 16777215, 16777215, 5000), "Min": ((0, 0), (0, 0), 1, 0, 0, 0),
"Default": "(1000 1000) (4000 4000) 7 #81ff65 #191919 2",
"Indication": "(départ) (arrivée) iterations couleur_trait couleur_fond stroke", "ParseData": "ppixxi"},
"dragon": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 19, 16777215, 16777215, 5000), "Min": ((0, 0), 1, 1, 0, 0, 0),
"Default": "(2500 2500) 4 18 #81ff65 #191919 2", "Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
"ParseData": "pfixxi"},
"sierpinski_triangle": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 11, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
"Default": "(0 0) 10 9 #81ff65 #191919 2", "Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
"ParseData": "pfixxi"},
"fractal_plant": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 8, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
"Default": "(0 2500) 6.5 8 #81ff65 #191919 2", "Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
"ParseData": "pfixxi"},
"koch_curve_right_angle": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 9, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
"Default": "(0 5000) 2.25 7 #81ff65 #191919 2",
"Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
"ParseData": "pfixxi"},
"fractal_binary_tree": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 15, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
"Default": "(0 2500) 1.2 12 #81ff65 #191919 2", "Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
"ParseData": "pfixxi"}
}
self.help = """\
</prefix>fractale info <fractale>
=> Affiche les informations relatives à la fractale spécifiée. (paramètres attendus, paramètres par défaut, type des arguments)
</prefix>fractale <fractale> <arguments>
=> Génère une image fractale à partir des arguments fournis. Pour mettre la valeur par défaut pour un des arguments, le remplacer par le caractère *
</prefix>fractale <fractale>
=> Génère une image fractale avec les paramètres par défaut.
-> Valeurs possible pour <fractale>
```..: Toutes les fractales:
%s```""" % '\n'.join(['......: %s' % t for t in self.fractals.keys()])
async def on_message(self, message):
async with message.channel.typing():
args = message.content.split(" ")
if len(args) == 1:
await self.modules['help'][1].send_help(message.channel, self)
elif len(args) == 2:
if args[1] in self.fractals.keys():
await self.send_fractal(message, args[1]+' '+' '.join(['*']*len(self.fractals[args[1]]['ParseData'])))
else:
await self.modules['help'][1].send_help(message.channel, self)
elif len(args) == 3:
if args[1] == "info" and args[2] in self.fractals.keys():
description = """\
La fractale {nom} attend les arguments suivant :
`{arguments}`
avec le type suivant:
`{type}`
Les nombres décimaux sont des nombres à virgules là où les nombres entiers n'en ont pas. **(mettre un point à la place de la virgule)**
Attention, les coordonnées des points doivent être entre parentheses et sont considérées comme un unique argument.
Gardez aussi en tête que **l'image a pour résolution 5000*5000.**
Les arguments valent par défaut :
`{defaut}`""".format(nom=args[2], arguments=self.fractals[args[2]]['Indication'], type=' '.join(["point decimal entier hexadecimal".split(" ")["pfix".index(ch)] for ch in self.fractals[args[2]]['ParseData']]), defaut=self.fractals[args[2]]['Default'])
await message.channel.send(
embed=discord.Embed(title="[%s] - Infos : *%s*" % (self.name, args[2]), description=description, color=self.color))
else:
await self.modules['help'][1].send_help(message.channel, self)
elif args[1] in self.fractals.keys():
await self.send_fractal(message, ' '.join(args[1:]))
else:
await self.modules['help'][1].send_help(message.channel, self)
async def send_fractal(self, *args):
await self.client.loop.run_in_executor(ThreadPoolExecutor(), self.send_fractal_async, *args)
async def send_fractal_async(self, message, command):
parsed_data=self.parse(command)
if parsed_data["Success"]:
res=parsed_data["Result"]
tmpstr="/tmp/%s.png"%random.randint(1,10000000)
im=Image.new('RGB', (5000,5000), tuple([int(i, 16) for i in map(''.join, zip(*[iter(hex(res[-2])[2:].ljust(6,'0'))]*2))]))
fig = eval("fractale.source.main."+self.fractals[command.split(' ')[0]]["Type"]+"(im=im)")
if self.fractals[command.split(' ')[0]]["Type"]=="Lsystem":
fig.state.x,fig.state.y=res[0]
#fig.state.color,fig.state.width=tuple([int(i, 16) for i in map(''.join, zip(*[iter(hex(res[-3])[2:].ljust(6,'0'))]*2))]),res[-1]
eval("fig."+command.split(' ')[0])(*res[1:-3],color=tuple([int(i, 16) for i in map(''.join, zip(*[iter(hex(res[-3])[2:].ljust(6,'0'))]*2))]), width=res[-1])
else:
eval("fig."+command.split(' ')[0])(*res[:-3],color=tuple([int(i, 16) for i in map(''.join, zip(*[iter(hex(res[-3])[2:].ljust(6,'0'))]*2))]), width=res[-1])
im.save(tmpstr)
await message.channel.send(file=discord.File(tmpstr))
else:
await message.channel.send(parsed_data["Message"])
def parse(self, inp):
retDic = {"Success": False, "Message": "", "Result": ()}
# Parsing the fractal name and storing the corresponding dic into a variable
try:
fractal = self.fractals[inp.split(' ')[0]]
except KeyError:
retDic.update({"Success": False, "Message": "La fractale %s n'existe pas." % inp.split(' ')[0]})
return (retDic)
arg = ' '.join(inp.split(' ')[1:]) # Stuff after the fractal name
# checking for incoherent parentheses usage
parentheses_count = 0
for i, char in enumerate(arg):
if char == '(':
parentheses_count += 1
elif char == ')':
parentheses_count -= 1
if not (-1 < parentheses_count < 2):
retDic.update({"Success": False,
"Message": "Usage invalide de parentheses au charactère numéro %s (à partir d'après le nom de la fractale)." % i})
return (retDic)
# Here, we have a coherent parentheses usage
if ',' in arg:
retDic.update({"Success": False,
"Message": "Les virgules n'ont pas leur place dans les paramètres de génération. Il ne doit y avoir que des espaces uniques."})
return (retDic)
# parsing the fractal
args = arg.replace(')', '').replace('(', '').split(' ')
parsed_args = []
i = 0
for parse in fractal['ParseData']:
if parse == 'p':
if args[i] != '*':
try:
int(args[i])
int(args[i + 1])
except:
retDic.update({"Success": False,
"Message": "Les valeurs ne sont pas du bon type. (nombre entiers attendus pour les coordonnées d'un point)"})
return (retDic)
parsed_args.append((int(args[i]), int(args[i + 1])))
i += 2
else:
parsed_args.append(
self.parse(inp.split(' ')[0] + ' ' + fractal['Default'])['Result'][len(parsed_args)])
i += 1
elif parse == 'f':
if args[i] != '*':
try:
float(args[i])
except:
retDic.update({"Success": False,
"Message": "Les valeurs ne sont pas du bon type. (Nombre à virgule flottante attendu (mettre un point pour la virgule))"})
return (retDic)
parsed_args.append(float(args[i]))
i += 1
else:
parsed_args.append(
self.parse(inp.split(' ')[0] + ' ' + fractal['Default'])['Result'][len(parsed_args)])
i += 1
elif parse == 'i':
if args[i] != '*':
try:
int(args[i])
except:
retDic.update({"Success": False,
"Message": "Les valeurs ne sont pas du bon type. (Nombre entier attendu)"})
return (retDic)
parsed_args.append(int(args[i]))
i += 1
else:
parsed_args.append(
self.parse(inp.split(' ')[0] + ' ' + fractal['Default'])['Result'][len(parsed_args)])
i += 1
elif parse == 'x':
if args[i] != '*':
try:
if '#' in args[i]:
int(args[i].replace('#', '0x'), 16)
else:
raise
except:
retDic.update({"Success": False,
"Message": "Les valeurs ne sont pas du bon type. (Valeur hexadécimale attendue)"})
return (retDic)
parsed_args.append(int(args[i].replace('#', '0x'), 16))
i += 1
else:
parsed_args.append(
self.parse(inp.split(' ')[0] + ' ' + fractal['Default'])['Result'][len(parsed_args)])
i += 1
for i,element in enumerate(parsed_args):
if element>fractal['Max'][i]:
retDic.update({"Success": False,
"Message": "Les valeurs sont trop grandes (%s > %s)"%(str(element),str(fractal['Max'][i]))})
return retDic
elif element<fractal['Min'][i]:
retDic.update({"Success": False,
"Message": "Les valeurs sont trop petites (%s < %s)"%(str(element),str(fractal['Min'][i]))})
return retDic
retDic.update({"Success": True, "Result": parsed_args})
return (retDic)