From 1e9fd8998029cf2927c93cc247af6a4f624b0fff Mon Sep 17 00:00:00 2001 From: ddenoncin Date: Mon, 10 Jul 2023 16:20:28 +0200 Subject: [PATCH] initial commit --- LICENSE | 7 +- README.md | 16 +- TODO.md | 5 + etude_courbe/__init__.py | 1 + etude_courbe/constantes_etude_courbe.py | 1 + etude_courbe/etude_courbe.py | 106 +++++ etude_courbe/lib_etude_courbe.py | 488 ++++++++++++++++++++++++ setup.py | 31 ++ 8 files changed, 652 insertions(+), 3 deletions(-) create mode 100644 TODO.md create mode 100644 etude_courbe/__init__.py create mode 100644 etude_courbe/constantes_etude_courbe.py create mode 100644 etude_courbe/etude_courbe.py create mode 100644 etude_courbe/lib_etude_courbe.py create mode 100644 setup.py diff --git a/LICENSE b/LICENSE index 7a3094a..eadf858 100644 --- a/LICENSE +++ b/LICENSE @@ -1,11 +1,14 @@ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 Copyright (C) 2004 Sam Hocevar -Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. +Everyone is permitted to copy and distribute verbatim or modified copies of +this license document, and changing it is allowed as long as the name is changed. DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - 0. You just DO WHAT THE FUCK YOU WANT TO. + 0. You just DO WHAT THE FUCK YOU WANT TO. \ No newline at end of file diff --git a/README.md b/README.md index ee0708f..33ddb54 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ -# etude_courbe +# Étude fonctions +Permet de générer automatiquement le code LaTeX permettant l'étude d'une courbe. + +## Utilisation + +Exécuter en ligne de commande le script ``etude_fonction`` qui vous demandera un dossier dans lequel mettre les sources LaTeX ainsi que l'expression de la fonction à écrire au format ``Python`` (préfixer par ``sp`` les fonctions usuelles) puis les intervalles sur lesquels la fonction est définie, dérivable et sur lequel vous voulez son tableau de variation. Les intervalles doivent être de la forme ``[a,b]`` ou ``]a,b[`` ou ``[a,b[`` ou ``]a,b]`` avec ``a`` et ``b`` convertibles en flottant ou bien les symboles ``-oo`` ou ``oo``. + +Vous obtiendrez alors le ``main.tex`` du dossier voulu que vous pourrez compiler. Pour ajouter des informations, éditer simplement le fichier ``example.tex``. Pour éditer l'en-tête, il suffit d'éditer le fichier ``Utils/02_config_paquets.tex``. + +## Contributions + +Toute contribution est la bienvenue, et peut m'être adressée à l'adresse + +## Licence +[WTFPL](http://www.wtfpl.net/) diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..19a8d91 --- /dev/null +++ b/TODO.md @@ -0,0 +1,5 @@ +## TODO + +Faire une documentation correcte... + +Écrire des tests ? diff --git a/etude_courbe/__init__.py b/etude_courbe/__init__.py new file mode 100644 index 0000000..79e5ce0 --- /dev/null +++ b/etude_courbe/__init__.py @@ -0,0 +1 @@ +from etude_courbe.etude_courbe import * diff --git a/etude_courbe/constantes_etude_courbe.py b/etude_courbe/constantes_etude_courbe.py new file mode 100644 index 0000000..50a32f4 --- /dev/null +++ b/etude_courbe/constantes_etude_courbe.py @@ -0,0 +1 @@ +version = '0.4' diff --git a/etude_courbe/etude_courbe.py b/etude_courbe/etude_courbe.py new file mode 100644 index 0000000..aa4823e --- /dev/null +++ b/etude_courbe/etude_courbe.py @@ -0,0 +1,106 @@ +from . import lib_etude_courbe as libec +import libargfunc as libargv +import os +import libcurses as curses +from sys import argv,exit +import documentex +from subprocess import call +from pylatex import Document, Section, Itemize, Command, Math, NoEscape +from pylatex.base_classes import Arguments +from sympy import oo + +def etude_courbe(): + + if len(argv) != 2: + print("usage : etude_courbe nom_dossier") + exit(1) + + dossier = argv[1] + + ok = False + while not ok: + x = curses.saisie_message_std('x(t)= ? ') + x, ok = libargv.sympify(x) + if not ok: + curses.afficher_message_std(x) + # en sortie, expression est une expression sympy utilisable + ok = False + while not ok: + y = curses.saisie_message_std('y(t)= ? ') + y, ok = libargv.sympify(y) + if not ok: + curses.afficher_message_std(y) + + ok = False + while not ok: + intervalle_etude = curses.saisie_message_std("intervalle d'étude ? ") + intervalle_etude, ok = libargv.sympify_intervalle(intervalle_etude) + if not ok: + curses.afficher_message_std(intervalle_etude) + + develop = ( curses.saisie_message_std("Voulez-vous également l'étude et le tracé de la développée ? (o/n) ") == 'o' ) + + f = libec.courbe(x,y,intervalle_etude) + + xmin=-10 + xmax=10 + ymin=-10 + ymax=10 + if f.i.inf != -oo: + tmin = round(float(f.i.inf),2) + else: + tmin = -10 + if f.i.sup != oo: + tmax = round(float(f.i.sup),2) + else: + tmax = 10 + + numero=str(1) + numerodev=str(int(numero)+1) + call(['documentex',dossier,'doc']) + currdir = os.getcwd() + os.chdir(os.path.join(dossier)) + f.graphe(xmin,xmax,ymin,ymax,tmin,tmax,n=1000) + if develop: + dev = f.developpee() + libec.graphe([f,dev],xmin,xmax,ymin,ymax,tmin,tmax,nom_pdf='courbe_dev.pdf',show=False) + os.chdir(currdir) + + + doc = Section(NoEscape(r"\'Etude d'une courbe")) + doc.append("Nous étudions la courbe donnée par") + doc.append(f.latex_print()) + doc.append("Les fonctions de coordonnées sont dérivables et données par") + doc.append(f.df().latex_print("'")) + tableau_variation = f.tikztabvar() + if tableau_variation: + doc.append("On en déduit le tableau de variation suivant") + doc.append(tableau_variation) + etude,var = f.points_particuliers() + if var: + doc.append("De ce tableau de variations on déduit les informations suivantes") + doc.append(etude) + else: + doc.append("<+sympy.solve et sympy.solveset sont malheureusement incapables de trouver les points critiques des fonctions de coordonnées pour une raison qui m'échappe+>") + doc.append(NoEscape(r"Le graphique de la courbe est donné sur la figure \ref{fig:"+numero+"}")) + doc.append(f.display_graphe(r'Graphique de la courbe',numero)) + if develop: + doc.append(r'La courbure de la courbe est donnée par') + with doc.create(Math(escape=False)) as courbure: + courbure.append(libec.latex(f.courbure())) + doc.append(r'Sa développée est alors donnée par') + doc.append(dev.latex_print("_d")) + doc.append(r"L'étude de la développée donne alors le tableau de variation suivant") + tabvardev = dev.tikztabvar(der="_d") + if tabvardev: + doc.append(tabvardev) + etude,var = dev.points_particuliers() + if var: + doc.append("On a alors les informations suivantes") + doc.append(etude) + else: + doc.append("<+sympy.solve et sympy.solveset sont malheureusement incapables de trouver les points critiques des fonctions de coordonnées pour une raison qui m'échappe+>") + doc.append(NoEscape(r"On peut alors représenter sur un même graphique la courbe et sa développée, voir figure \ref{fig:"+numerodev+"}")) + doc.append(libec.display_graphe([f,dev],r'La courbe et sa développée',numerodev,nom_pdf='courbe_dev.pdf')) + # doc.append(display_graphe([f],r'La courbe',numerodev,xmin,xmax,ymin,ymax,tmin,tmax,nom_pdf='courbe_dev.pdf',show=False)) + doc.generate_tex(os.path.join(dossier,'example')) diff --git a/etude_courbe/lib_etude_courbe.py b/etude_courbe/lib_etude_courbe.py new file mode 100644 index 0000000..38ffca9 --- /dev/null +++ b/etude_courbe/lib_etude_courbe.py @@ -0,0 +1,488 @@ +import matplotlib +matplotlib.use('Agg') +from pylatex import Itemize, Command, NoEscape, Alignat +from pylatex.base_classes import Arguments, Environment, Options +from sympy import Matrix,simplify,latex,diff,oo,limit,FiniteSet,lambdify,degree,S,det,sqrt +from sympy.abc import t,x +from numpy import linspace +import matplotlib.pyplot as plt +import sys +from etude_fonction.lib_etude_fonction import Fonction, float_or_tex, order + + + +#classes utiles pour les environnements latex utilisés +class Array(Environment):#,options=None,Arguments=None,start_arguments=None,): + escape=False +class Displaymath(Environment): + escape=False +class Figure(Environment): + escape=False +class Tikzpicture(Environment): + escape=False +class Cases(Environment): + escape=False +class Pmatrix(Environment): + escape=False + +def pmatrix(liste): + """ + remplit le vecteur colonne à partir d'une liste de chaînes de caractères + """ + matrix=Pmatrix() + n = len(liste) + for i in range(n): + element = liste[i] + if i!=n-1: + matrix.append(NoEscape(element + r"\\")) + else: + matrix.append(NoEscape(element)) + return matrix + +def norme(x,y): + """ + retourne la norme euclidienne du vecteur de coordonnées (x,y) + """ + return sqrt(x**2+y**2) + +class courbe(): + """ + classe permettant d'étudier/tracer des courbes pas trop compliquées du plan. À améliorer : + - ajouter l'étude d'asymptotes + - problème avec la résolution de sinh=0 traitée pour le moment de façon ad hoc uniquement sur les abscisses + - renvoyer des string plutôt que faire des print + - conversion radian-degré pour les fonctions trigo réciproques + - ajouter des tangentes sur le graphique + """ + def __init__(self,x,y,i): + """ + Une courbe est l'ensemble des deux fonctions coordonnées et d'un Interval. Elle doit être continue sur cet l'intervalle correspondant. + """ + self.x=x + self.y=y + self.i=i #i est un intervalle + def __str__(self): + return str(self.x)+','+str(self.y)+','+str(self.i) + def latex_print(self,der=''): + """ + Permet de donner le code latex représentant la courbe par ses fonctions coordonnées. der est en général un ' + """ + align = Alignat(escape=False,numbering=False) + with align.create(Cases()) as cas: + cas.append(NoEscape(r"x"+der+"(t)&="+latex(self.x)+r"\\")) + cas.append(NoEscape(r"y"+der+"(t)&="+latex(self.y)+r"\\")) + return align + def fx(self): + return Fonction(self.x,self.i,S.Reals) + def fy(self): + return Fonction(self.y,self.i,S.Reals) + def subs(self,symbol): + return courbe(self.x.subs(t,symbol),self.y.subs(t,symbol),self.i) + def dx(self): + """ + retourne sous forme d'expression symbolique la dérivée de la première fonction coordonnée + """ + return diff(self.x,t).simplify() + def dy(self): + """ + retourne sous forme d'expression symbolique la dérivée de la deuxième fonction coordonnée + """ + return diff(self.y,t).simplify() + def df(self): + """ + retourne sous forme d'une courbe la dérivée de la courbe + """ + return courbe(self.dx(),self.dy(),self.i) + def courbure(self): + """ + Retourne la courbure en un point birégulier comme le déterminant de vitesse accélération divisé par la norme de la vitesse au cube + """ + vitesse = self.df() + acceleration = vitesse.df() + numerateur = det(Matrix([[vitesse.x,acceleration.x],[vitesse.y,acceleration.y]])) + denominateur = (norme(vitesse.x,vitesse.y).simplify())**3 + return (numerateur/denominateur).simplify() + def rayon_courbure(self): + """ + nom trompeur, c'est en fait le rapport lambda(t) tel que le point (x(t),y(t))+lambda(t) (-y'(t),x(t)) soit la développée de la courbe + """ + return ((self.dx())**2+(self.dy())**2).simplify()/det(Matrix([[self.dx(),self.df().dx()],[self.dy(),self.df().dy()]])).simplify() + def developpee(self): + """ + retourne sous forme de courbe la développée de la courbe initiale + """ + return courbe((self.x-self.rayon_courbure()*self.dy()).simplify(),(self.y+self.rayon_courbure()*self.dx()).simplify(),self.i) + def graphe(self,xmin,xmax,ymin,ymax,tmin,tmax,nom_pdf='courbe.pdf',n=1000): + """ + trace le graphique de la courbe et le sauvegarde dans le dossier courant sous le nom courbe.pdf + """ + ax=plt.gca() + ax.axis((xmin,xmax,ymin,ymax)) + ax.set_aspect('equal') + ax.spines['right'].set_color('none') + ax.spines['top'].set_color('none') + ax.xaxis.set_ticks_position('bottom') + ax.spines['bottom'].set_position(('data',0)) + ax.yaxis.set_ticks_position('left') + ax.spines['left'].set_position(('data',0)) + fx=lambdify((t),self.x,'numpy') + fy=lambdify((t),self.y,'numpy') + T=linspace(tmin,tmax,n) + X=[fx(t) for t in T] + Y=[fy(t) for t in T] + plt.plot(X,Y) + plt.savefig(nom_pdf) + def display_graphe(self,titre,numero,nom_pdf='courbe.pdf',position='H'): + #self.graphe(xmin,xmax,ymin,ymax,tmin,tmax,nom_pdf=nom_pdf,n=1000) + graphique = Figure(options=Options('H')) + graphique.append(r'\centering') + graphique.append(Command('includegraphics',arguments=Arguments(nom_pdf))) + arg = Arguments(NoEscape(titre+r'\label{fig:'+numero+'}')) +# arg.append(Command('label',arguments=Arguments('fig:'+numero))) + c = Command('caption',arguments=arg) + graphique.append(c) + return graphique + + def series_latex(self,x0=0,n=3,dir='+',logx=None): + """ + retourne le code latex d'un développement limité de la courbe avec au moins trois termes non nuls. + """ + dlx = self.x.series(t,x0,n=None) + dly = self.y.series(t,x0,n=None) + dlxlist = [] + dlylist = [] + for i in range(n): + try: + ndlx = next(dlx) + try: + dlxlist+= [[ndlx/(t-x0)**degree(ndlx,t),degree(ndlx,t)]] + except: + dlxlist+= [[ndlx,0]] + except StopIteration: + #print('pas de terme après le '+str(i+1)+'ème') + dlxlist+=[[0,degx+1]] ##suppose que la boucle fonctionne toujours au moins une fois sous peine de degx undefined global variable + try: + ndly = next(dly) + try: + dlylist+= [[ndly/(t-x0)**degree(ndly,t),degree(ndly,t)]] + except: + dlylist+= [[ndly,0]] + except StopIteration: + #print('pas de terme après le '+str(i+1)+'ème') + dlylist+=[[0,degy+1]] + degx = dlxlist[-1][1] + degy = dlylist[-1][1] + # try: ### avant modif + # ndlx = next(dlx) + # ndly = next(dly) + # try: + # dlxlist+= [[ndlx/(t-x0)**degree(ndlx,t),degree(ndlx,t)]] + # except: + # dlxlist+= [[ndlx,0]] + # try: + # dlylist+= [[ndly/(t-x0)**degree(ndly,t),degree(ndly,t)]] + # except: + # dlylist+= [[ndly,0]] + # except StopIteration: + # print('pas de terme après le '+str(i+1)+'ème') + # break + + align = Alignat(escape=False,numbering=False) + with align.create(Pmatrix()) as matrix: + matrix.append(NoEscape(r"x(t)\\ y(t)")) + align.append(NoEscape(r"=")) + l=len(dlxlist) + i=0 + while len(dlxlist)>0 and len(dlylist)>0: + if dlxlist[i][1]dlylist[i][1]: + degre=dlylist[i][1] + if dlylist[i][1]==0: + align.append(pmatrix([r"0",latex(dlylist[i][0])])) + align.append(NoEscape(r"+")) + else: + align.append(pmatrix([r"0",latex(dlylist[i][0])])) + if x0==0: + align.append(NoEscape(r"t^{"+latex(dlylist[i][1])+'}+')) + else: + align.append(NoEscape(r"\left(t-"+latex(x0)+r'\right)^{'+latex(dlylist[i][1])+'}+')) + dlylist.pop(0) + else: + degre=dlxlist[i][1] + if dlylist[i][1]==0: + align.append(pmatrix([latex(dlxlist[i][0]),latex(dlylist[i][0])])) + align.append(r"+") + else: + align.append(pmatrix([latex(dlxlist[i][0]),latex(dlylist[i][0])])) + if x0==0: + align.append(NoEscape(r't^{'+latex(dlylist[i][1])+'}+')) + else: + align.append(NoEscape(r"\left(t-"+latex(x0)+r'\right)^{'+latex(dlylist[i][1])+'}+')) + dlylist.pop(0) + dlxlist.pop(0) + if x0==0: + align.append(NoEscape(r'o\left(t^{'+latex(degre)+r'}\right)')) + else: + align.append(NoEscape(r'o\left(\left(t-'+latex(x0)+r'\right)^{'+latex(degre)+r'}\right)')) + return align + def indices_fondamentaux(self,x0=0): + """ + renvoie une liste comportant 0, 1 ou les deux premiers indices fondamentaux et un message d'erreur s'il n'est pas possible d'obtenir les deux premiers indices fondamentaux. + """ + dlx = self.x.series(t,x0,n=None) + dly = self.y.series(t,x0,n=None) + dlxlist = [] + dlylist = [] + for i in range(3): + try: + ndlx = next(dlx) + try: + dlxlist+= [[ndlx/(t-x0)**degree(ndlx,t),degree(ndlx,t)]] + except: + dlxlist+= [[ndlx,0]] + except StopIteration: + #print('pas de terme après le '+str(i+1)+'ème') + dlxlist+=[[0,degx+1]] ##suppose que la boucle fonctionne toujours au moins une fois sous peine de degx undefined global variable + try: + ndly = next(dly) + try: + dlylist+= [[ndly/(t-x0)**degree(ndly,t),degree(ndly,t)]] + except: + dlylist+= [[ndly,0]] + except StopIteration: + #print('pas de terme après le '+str(i+1)+'ème') + dlylist+=[[0,degy+1]] + degx = dlxlist[-1][1] + degy = dlylist[-1][1] + + dlxlist = [item[1] for item in dlxlist] + dlylist = [item[1] for item in dlylist] +# for i in range(3): +# try: +# ndlx = next(dlx) +# ndly = next(dly) +# try: +# dlxlist+= [degree(ndlx,t)] +# except: +# dlxlist+= [0] +# try: +# dlylist+= [degree(ndly,t)] +# except: +# dlylist+= [0] +# except StopIteration: +# print('pas de terme après le '+str(i+1)+'ème') +# break + indices=[] + while len(indices)<2: + try: + if dlxlist[0]0: + indices.append(dlxlist[0]) + dlxlist.pop(0) + else: + dlxlist.pop(0) + elif dlylist[0]0: + indices.append(dlylist[0]) + dlylist.pop(0) + else: + dlylist.pop(0) + else: + if dlylist[0]>0: + indices.append(dlylist[0]) + dlylist.pop(0) + dlxlist.pop(0) + else: + dlylist.pop(0) + dlxlist.pop(0) + except: + break + return indices + def type_point(self,x0=0): + """ + utilise les deux indices fondamentaux pour renvoyer le type de point sous forme de chaine de caractère, ou un message expliquant qu'il n'y a pas assez d'informations pour conclure. + """ + indices = self.indices_fondamentaux(x0) + if len(indices)==2: + if indices[0]%2==1 and indices[1]%2==0: + if indices[0]==1 and indices[1]==2: + return "point birégulier" + else: + return "point ordinaire" + if indices[0]%2==1 and indices[1]%2==1: + return "point d'inflexion" + if indices[0]%2==0 and indices[1]%2==1: + return "point de rebroussement de première espèce" + if indices[0]%2==0 and indices[1]%2==0: + return "point de rebroussement de deuxième espèce" + else: + return "point pour lequel il n'y a pas assez d'informations pour conclure" + def set_solf(self): + """ + retourne les zéros des dérivées de la fonction vectorielle f de la variable t ainsi que les bornes de son intervalle de définition + """ + f = self.subs(x) + critiquex = f.fx().critique(f.fx().s) + critiquey = f.fy().critique(f.fy().s) + if critiquex==-1 or critiquey==-1: + return -1 + else: + return critiquex.union(critiquey).intersection(self.i).union(FiniteSet(self.i.sup)).union(FiniteSet(self.i.inf)) + def intervalle_tabvar(self): + """ + donne le code latex de la première ligne d'un tableau de variation de f + """ + set_sol=self.set_solf() + try: + set_sol=order(set_sol) + sol='' + for s in set_sol: + if s == oo: + sol+='$' + sol+='+'+latex(s) + sol+='$' + elif s == set_sol[-1]: + sol+='$' + sol+=latex(s) + sol+='$' + else: + sol+='$' + sol+=latex(s) + sol+='$,' + return sol + except: + return -1 + def signes(self,fun,eps=10**(-1)): + """ + fonction permettant de remplir avec tikz un tableau de signes : on trouve les valeurs intéressantes de la courbe, celles d'une des fonction de coordonnées et on établit un tableau de signe en remplissant deux cases de suite grâce à la fonction signe en partant d'une valeur intéressante de la courbe (borne ou zéro d'une des dérivées). + """ + try: + set_sol=order(self.set_solf()) + set_solfu=order(fun.subs(t,x).critique(fun.s)) + signx='' + n=len(set_sol) + for i in range(n-1): + sol1=set_sol[i] + sol2=set_sol[i+1] + signx+=fun.subs(t,x).dx(self.i).signe(eps,sol1,sol2,set_solfu) + sol=set_sol[n-1] + if sol in set_solfu: + signx+='0' + return signx + except: + return -1 + def variations(self,fun,eps=10**(-1)): + """ + donne le code latex pour la ligne du tableau de variations d'une des fonctions de coordonnées en utilisant la fonction variation + """ + var='' + func = Fonction(fun.expr.subs(t,x),fun.s,fun.t) + set_sol = order(self.set_solf()) + n=len(set_sol) + set_solfu = order(func.critique(func.s)) + for i in range(n-1): + sol1=set_sol[i] + sol2=set_sol[i+1] + var+=func.variation(func.s,sol1,sol2,set_solfu,eps) + sol1=set_sol[n-2] + sol2=set_sol[n-1] + lim = limit(fun.expr,t,sol2,'-') + tex_lim = float_or_tex(lim) +# dfun = func.dx(func.s) + funx = (fun.subs(t,x)).dx(fun.s) + signeligne = funx.signe(eps,sol1,sol2,set_solfu) + if '+' in signeligne: + var+='+/$'+tex_lim+'$' + elif '-' in signeligne: + var+='-/$'+tex_lim+'$' + return var + def tikztabvar(self,der='',eps=10**(-1)): + """ + donne le code latex pour remplir le tableau de signes/variations de la courbe f ou une chaîne vide si cela n'est pas possible + """ + figure = Figure(options=Options('H')) + figure.append(Command('centering')) + try: + with figure.create(Tikzpicture()) as fig: + fig.append(Command('tkzTabInit',arguments=Arguments(NoEscape("$t$/1,$x"+der+"'(t)$/1,$x"+der+"$/1,$y"+der+"'(t)$/1,$y"+der+"$/1"),NoEscape(self.intervalle_tabvar())))) + fig.append(Command('tkzTabLine',arguments=Arguments(NoEscape(self.signes(self.fx(),eps))))) + fig.append(Command('tkzTabVar',arguments=Arguments(NoEscape(self.variations(self.fx(),eps))))) + fig.append(Command('tkzTabLine',arguments=Arguments(NoEscape(self.signes(self.fy(),eps))))) + fig.append(Command('tkzTabVar',arguments=Arguments(NoEscape(self.variations(self.fy(),eps))))) + return figure + except: # problème dans l'étude des signes ou des variations + return '' + def points_particuliers(self): + """ + génère le code latex décrivant la nature des points en lesquels il y a une tangente horizontale ou verticale, ainsi que la nature des points stationnaires en justifiant avec un développement limité. + """ + set_sol = order(self.set_solf()) + set_solx = order(self.fx().subs(t,x).critique(self.fx().s)) + set_soly = order(self.fy().subs(t,x).critique(self.fy().s)) + var = 0 + etude = Itemize() + for s in set_sol: + if s in set_solx and s not in set_soly: + etude.add_item(NoEscape(r'il y a une tangente verticale au point de paramètre $'+ latex(s)+'$')) + var=1 + elif s in set_soly and not s in set_solx: + etude.add_item(NoEscape(r'il y a une tangente horizontale au point de paramètre $'+latex(s)+'$')) + var=1 + elif s in set_soly and s in set_soly: + string = r'le point de paramètre $'+latex(s)+'$ est un '+self.type_point(s) + string += r' comme le montre de développement limité suivant au voisinage de $'+latex(t)+'='+latex(s)+'$\n' +# string += self.series_latex() + etude.add_item(NoEscape(string)) + etude.append(self.series_latex(s)) + var=1 + return [etude,var] + + + +def graphe(liste,xmin,xmax,ymin,ymax,tmin,tmax,nom_pdf='courbe.pdf',n=1000,show=True): + """ + trace sur un même graphique les courbes de la liste + """ + ax=plt.gca() + ax.axis((xmin,xmax,ymin,ymax)) + ax.set_aspect('equal') + ax.spines['right'].set_color('none') + ax.spines['top'].set_color('none') + ax.xaxis.set_ticks_position('bottom') + ax.spines['bottom'].set_position(('data',0)) + ax.yaxis.set_ticks_position('left') + ax.spines['left'].set_position(('data',0)) + for arc in liste: + fx=lambdify((t),arc.x,'numpy') + fy=lambdify((t),arc.y,'numpy') + T=linspace(tmin,tmax,n) + X=[fx(t) for t in T] + Y=[fy(t) for t in T] + plt.plot(X,Y) + if show: + plt.show() + plt.savefig(nom_pdf) + +def display_graphe(liste,titre,numero,nom_pdf='courbe.pdf',position='H'): + """ + Renvoie le code latex pour afficher les courbes de la liste + """ + #graphe(liste,xmin,xmax,ymin,ymax,tmin,tmax,nom_pdf=nom_pdf,n=n,show=show) + graphique = Figure(options=Options('H')) + graphique.append(r'\centering') + graphique.append(Command('includegraphics',arguments=Arguments(NoEscape(nom_pdf)))) + arg = Arguments(NoEscape(titre+r'\label{fig:'+numero+'}')) + c = Command('caption',arguments=arg) + graphique.append(c) + return graphique diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..919df64 --- /dev/null +++ b/setup.py @@ -0,0 +1,31 @@ +from setuptools import setup, find_packages +from etude_courbe.constantes_etude_courbe import version + +setup( + name="etude_courbe", + version=version, + packages=find_packages(), + include_package_data=True, + entry_points={ + 'console_scripts': [ + 'etude_courbe = etude_courbe.etude_courbe:etude_courbe', + ] + }, + install_requires=["libcurses", + "libargfunc", + "etude_fonction", + "documentex", + "sympy", + "pylatex", + "matplotlib", + "numpy", + ], + + author="David Denoncin", + author_email="math@denoncin.fr", + url="math.denoncin.fr", + description="Génération de corrigés d'étude de courbes", + classifiers=[ + "Licence :: WTFPL" + ] +)