Klafyvel
6 years ago
15 changed files with 1403 additions and 33 deletions
@ -0,0 +1,21 @@ |
|||
.PHONY: |
|||
.SUFFIXES: |
|||
|
|||
COMP = mdbg |
|||
OUT = cours |
|||
TITLE = "Microéconomie" |
|||
PACKAGE = "{{MyPack2}}" |
|||
AUTHOR = "Hugo LEVY--FALK" |
|||
DOCUMENTCLASS = "article" |
|||
|
|||
all: |
|||
$(COMP) $(OUT).mdbg --title $(TITLE) --packages $(PACKAGE) --date \\today --documentclass $(DOCUMENTCLASS) --author $(AUTHOR) |
|||
lualatex --shell-escape $(OUT).tex |
|||
lualatex --shell-escape $(OUT).tex |
|||
rm $(OUT).aux $(OUT).log $(OUT).out $(OUT).toc |
|||
rm $(OUT).tex |
|||
|
|||
clean: |
|||
rm $(OUT).aux $(OUT).log $(OUT).out $(OUT).toc |
|||
rm $(OUT).tex |
|||
|
@ -0,0 +1,155 @@ |
|||
\NeedsTeXFormat{LaTeX2e} |
|||
\ProvidesPackage{MyPack2} |
|||
|
|||
%%%%%%%% INCLUSIONS GÉNÉRALES %%%%%%%%% |
|||
\RequirePackage{babel} |
|||
%\RequirePackage{titlesec} |
|||
\RequirePackage{color} |
|||
\RequirePackage[a4paper]{geometry} |
|||
\RequirePackage[font=small,format=plain,labelfont=bf,up, |
|||
textfont=normal,up, |
|||
justification=justified, |
|||
singlelinecheck=false]{caption} |
|||
\RequirePackage[justification=centering]{caption} |
|||
\RequirePackage{xcolor} % Required for specifying custom colours |
|||
\definecolor{grey}{rgb}{0.9,0.9,0.9} % Colour of the box surrounding the title |
|||
\RequirePackage[utf8]{inputenc} % Required for inputting international characters |
|||
|
|||
\RequirePackage[T1]{fontenc} |
|||
% \RequirePackage[sfdefault]{ClearSans} % Use the Clear Sans font (sans serif) |
|||
\RequirePackage{placeins} |
|||
\RequirePackage{listings} |
|||
\RequirePackage{graphicx} |
|||
\RequirePackage{amsfonts} |
|||
|
|||
|
|||
|
|||
\definecolor{bleu}{RGB}{0,100,143} |
|||
\definecolor{gris}{RGB}{130,130,140} |
|||
\definecolor{grisBleu}{RGB}{65,115,141} |
|||
|
|||
|
|||
|
|||
%%%%%%%% MATH %%%%%%%%% |
|||
\RequirePackage{stmaryrd} |
|||
\newcommand*\intervalleEntier[2]{\intervalle{\llbracket}{#1}{#2}{\rrbracket}} |
|||
\newcommand*\intervalle[4]{\left#1 #2 \, ; #3 \right#4} |
|||
\newcommand*\intervalleOO[2]{\intervalle{]}{#1}{#2}{[}} |
|||
\newcommand*\intervalleFF[2]{\intervalle{[}{#1}{#2}{]}} |
|||
\newcommand*\intervalleOF[2]{\intervalle{]}{#1}{#2}{]}} |
|||
\newcommand*\intervalleFO[2]{\intervalle{[}{#1}{#2}{[}} |
|||
|
|||
\let\ensembleNombre\mathbb |
|||
\newcommand*\N{\ensembleNombre{N}} |
|||
\newcommand*\Z{\ensembleNombre{Z}} |
|||
\newcommand*\Q{\ensembleNombre{Q}} |
|||
\newcommand*\R{\ensembleNombre{R}} |
|||
\newcommand*\C{\ensembleNombre{C}} |
|||
|
|||
\newcommand*\enstq[2]{\left\{#1,\; #2\right\}} |
|||
|
|||
%\newcommand*\int[1]{\left\lfloor #1\right\rfloor} |
|||
%\newcommand*\norme[1]{\left\lVert#1\right\rVert} |
|||
%\newcommand*\abs[1]{\left\lvert#1\right\rvert} |
|||
|
|||
\let\vecteur\overrightarrow |
|||
|
|||
\newcommand*\Sys[1]{\left\{ \begin{aligned} #1 \end{aligned} \right. \kern-\nulldelimiterspace} |
|||
|
|||
\newcommand*\application[5]{ |
|||
#1 \colon |
|||
\begin{alignedat}{2}  &\to \\ |
|||
 &\mapsto  |
|||
\end{alignedat} |
|||
} |
|||
\newcommand*\applicationSigne[3]{ |
|||
#1 \colon #2 \to #3 |
|||
} |
|||
|
|||
\newtheorem{defi}{Définition}[section] |
|||
\newtheorem{theo}{Théorème}[section] |
|||
\newtheorem{prop}{Proposition}[section] |
|||
\newtheorem{ex}{Exemple}[section] |
|||
|
|||
|
|||
%%%%%%%% SECTIONNEMENT %%%%%%%%% |
|||
|
|||
%\renewcommand{\thesection}{\Roman{section}} |
|||
%\renewcommand{\thesubsection}{\arabic{subsection}} |
|||
|
|||
\newcommand*\emptyPage{\newpage\null\thispagestyle{empty}\addtocounter{page}{-1}\newpage} |
|||
|
|||
%%%%%%%% INTIALISATION DE LA PAGE %%%%%%%%% |
|||
\RequirePackage{fancyhdr} |
|||
\newcommand\initPage[3]{ |
|||
\pagestyle{fancy} |
|||
\fancyhead{#1} |
|||
\lhead{#2} |
|||
\rhead{#3} |
|||
\rfoot{} |
|||
\lfoot{} |
|||
|
|||
%\titleformat*{\section}{\Large\bfseries\color{bleu}\Roman{section}} |
|||
%\titleformat*{\subsection}{\large\bfseries\color{grisBleu}} |
|||
%\titleformat*{\subsubsection}{\bfseries\color{gris}} |
|||
%\titlespacing*{\section}{-3em}{*1}{*1} |
|||
%\titlespacing*{\subsection}{-2em}{*1}{*1} |
|||
%\titlespacing*{\subsubsection}{-1em}{*1}{*1} |
|||
|
|||
} |
|||
|
|||
%%%%%%%% TITRE %%%%%%%%M |
|||
\renewcommand{\maketitle}{ |
|||
\begin{titlepage} % Suppresses displaying the page number on the title page and the subsequent page counts as page 1 |
|||
%------------------------------------------------ |
|||
% Grey title box |
|||
%------------------------------------------------ |
|||
\topskip0pt |
|||
\vspace*{5cm} |
|||
\colorbox{grey}{ |
|||
\parbox[t]{0.93\textwidth}{ % Outer full width box |
|||
\parbox[t]{0.91\textwidth}{ % Inner box for inner right text margin |
|||
\raggedleft % Right align the text |
|||
\fontsize{72pt}{80pt}\selectfont % Title font size, the first argument is the font size and the second is the line spacing, adjust depending on title length |
|||
\vspace{1cm} % Space between the start of the title and the top of the grey box |
|||
|
|||
\@title |
|||
|
|||
\vspace{1cm} % Space between the end of the title and the bottom of the grey box |
|||
} |
|||
} |
|||
} |
|||
|
|||
\vfill % Space between the title box and author information |
|||
|
|||
%------------------------------------------------ |
|||
% Author name and information |
|||
%------------------------------------------------ |
|||
|
|||
\parbox[t]{0.93\textwidth}{ % Box to inset this section slightly |
|||
\raggedleft % Right align the text |
|||
\large % Increase the font size |
|||
{\Large \@author}\\[4pt] % Extra space after name |
|||
|
|||
\hfill\rule{0.2\linewidth}{1pt}\\% Horizontal line, first argument width, second thickness |
|||
\@date |
|||
} |
|||
|
|||
\end{titlepage} |
|||
|
|||
\emptyPage |
|||
} |
|||
|
|||
|
|||
|
|||
%%%%%%%% FIN DU DOCUMENT %%%%%%%%% |
|||
|
|||
\newcommand{\roadMap}{ |
|||
\clearpage |
|||
\listoffigures |
|||
\clearpage |
|||
\listoftables |
|||
} |
|||
|
|||
% Fin du package |
|||
\endinput |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 34 KiB |
@ -1,33 +0,0 @@ |
|||
class Graph: |
|||
"""Implémente un graphe non orienté.""" |
|||
|
|||
def __init__(self, nom): |
|||
"""Initialise un graphe vide. |
|||
|
|||
:param nom: Nom du graphe |
|||
""" |
|||
self.nom = nom |
|||
|
|||
|
|||
class Sommet: |
|||
"""Implémente un sommet de graphe.""" |
|||
|
|||
def __init__(self, pos): |
|||
"""Initialise un sommet. |
|||
|
|||
:param pos: couple donnant la position du point. |
|||
""" |
|||
self.pos = pos |
|||
|
|||
|
|||
class Arete: |
|||
"""Implémente une arête de graphe.""" |
|||
|
|||
def __init__(self, longueur, v_moyenne): |
|||
"""Initialise une arête. |
|||
|
|||
:param longueur: longueur de l'arête. |
|||
:param v_moyenne: vitesse moyenne sur l'arête. |
|||
""" |
|||
self.longueur = longueur |
|||
self.v_moyenne = v_moyenne |
@ -0,0 +1,316 @@ |
|||
import random |
|||
import triangulation |
|||
|
|||
|
|||
class Graphe: |
|||
"""Implémente un graphe non orienté.""" |
|||
|
|||
def __init__(self, nom): |
|||
"""Initialise un graphe vide. |
|||
|
|||
:param nom: Nom du graphe |
|||
""" |
|||
self.nom = nom |
|||
self.sommets = [] |
|||
self.aretes = [] |
|||
|
|||
def renomme(self, nom): |
|||
"""Renome le graphe |
|||
|
|||
:param nom: Nouveau nom du graphe. |
|||
""" |
|||
self.nom = nom |
|||
|
|||
def ajouteSommet(self, x, y): |
|||
"""Ajoute le sommet s au graphe. |
|||
|
|||
:param x: abcisse du sommet. |
|||
:param y: ordonnée du sommet. |
|||
|
|||
:return: Le sommet créé. |
|||
""" |
|||
s = Sommet((x, y), len(self.sommets)) |
|||
self.sommets.append(s) |
|||
return s |
|||
|
|||
def connecte(self, s1, s2, longueur, v_moyenne): |
|||
"""Connecte les sommets s1 et s2. |
|||
|
|||
:param s1: premier sommet. |
|||
:param s2: deuxième sommet. |
|||
:param longueur: longueur de l'arrête. |
|||
:param v_moyenne: vitesse moyenne sur l'arrête. |
|||
|
|||
:return: L'arête créée. |
|||
""" |
|||
a = Arete(s1, s2, longueur, v_moyenne) |
|||
self.aretes.append(a) |
|||
return a |
|||
|
|||
def n(self): |
|||
"""Retourne le nombre de sommets du graphe.""" |
|||
return len(self.sommets) |
|||
|
|||
def m(self): |
|||
"""Retourne le nombre d'arrêtes du graphe.""" |
|||
return len(self.aretes) |
|||
|
|||
def __str__(self): |
|||
return "V({nom})=[\n{noeuds}\n]\nE({nom})=[\n{aretes}\n]\n".format( |
|||
nom=self.nom, |
|||
aretes='\n'.join([str(v) for v in self.aretes]), |
|||
noeuds='\n'.join([str(n) for n in self.sommets]) |
|||
) |
|||
|
|||
def trace(self, dest): |
|||
"""Affiche le graphe sur la destination. |
|||
|
|||
:param dest: instance de Afficheur. |
|||
""" |
|||
for s in self.sommets: |
|||
dest.changeCouleur(s.couleur) |
|||
dest.tracePoint((s.x(), s.y())) |
|||
dest.traceTexte((s.x(), s.y()), 'v'+str(s.num)) |
|||
|
|||
for a in self.aretes: |
|||
dest.changeCouleur(a.couleur) |
|||
dest.traceLigne((a.s1.x(), a.s1.y()), (a.s2.x(), a.s2.y())) |
|||
|
|||
def ajouteRoute(self, v1, v2, vmax): |
|||
"""Ajoute une route de distance euclidienne entre v1 et v2. |
|||
|
|||
:param v1: Premier sommet. |
|||
:param v2: Deuxième sommet. |
|||
:param vmax: vitesse maximale sur la route. |
|||
|
|||
:return: Route créée. |
|||
""" |
|||
return self.connecte(v1, v2, v1.distance(v2), vmax) |
|||
|
|||
def ajouteNationale(self, v1, v2): |
|||
"""Ajoute une nationale au graphe. (rouge et vmax = 90km/h). |
|||
|
|||
:param v1: Premier sommet. |
|||
:param v2: Deuxième sommet. |
|||
|
|||
:return: route créée. |
|||
""" |
|||
a = self.ajouteRoute(v1, v2, 90) |
|||
a.couleur = (1., 0., 0.) |
|||
return a |
|||
|
|||
def ajouteDepartementale(self, v1, v2): |
|||
"""Ajoute une départementale au graphe. (jaune et vmax = 60km/h). |
|||
|
|||
:param v1: Premier sommet. |
|||
:param v2: Deuxième sommet. |
|||
|
|||
:return: route créée. |
|||
""" |
|||
a = self.ajouteRoute(v1, v2, 60) |
|||
a.couleur = (1., 1., 0.) |
|||
return a |
|||
|
|||
|
|||
class Sommet: |
|||
"""Implémente un sommet de graphe.""" |
|||
|
|||
def __init__(self, pos, num, couleur=(0., 0., 0.)): |
|||
"""Initialise un sommet. |
|||
|
|||
:param pos: couple donnant la position du point. |
|||
:param num: numéro du sommet. |
|||
:param couleur: couleur du sommet. |
|||
""" |
|||
self.pos = pos |
|||
self.num = num |
|||
self.couleur = couleur |
|||
|
|||
def __str__(self): |
|||
return "v{} (x = {} km y = {} km)".format( |
|||
str(self.num), |
|||
str(self.x()), |
|||
str(self.y()) |
|||
) |
|||
|
|||
def distance(self, v): |
|||
"""Calcule la distance entre deux points. |
|||
|
|||
:param v: Point de calcul de la distance. |
|||
""" |
|||
return ((self.x()-v.x())**2 + (self.y()-v.y())**2)**(1/2) |
|||
|
|||
def x(self): |
|||
"""Retourne l'abcisse de x.""" |
|||
return self.pos[0] |
|||
|
|||
def y(self): |
|||
"""Retourne l'ordonnée de y.""" |
|||
return self.pos[1] |
|||
|
|||
|
|||
class Arete: |
|||
"""Implémente une arête de graphe.""" |
|||
|
|||
def __init__(self, s1, s2, longueur, v_moyenne, couleur=(0., 0., 0.)): |
|||
"""Initialise une arête. |
|||
|
|||
:param s1: sommet 1. |
|||
:param s2: sommet 2. |
|||
:param longueur: longueur de l'arête. |
|||
:param v_moyenne: vitesse moyenne sur l'arête. |
|||
:param couleur: couleur de l'arête. |
|||
""" |
|||
self.s1 = s1 |
|||
self.s2 = s2 |
|||
self.longueur = longueur |
|||
self.v_moyenne = v_moyenne |
|||
self.couleur = couleur |
|||
|
|||
def voisin(self, s): |
|||
"""Retourne le sommet voisin de s dans l'arête. |
|||
|
|||
:param s: Un sommet de l'arête. |
|||
""" |
|||
if s == self.s1: |
|||
return self.s2 |
|||
else: |
|||
return self.s1 |
|||
|
|||
def __str__(self): |
|||
return " v{v1}, v{v2} (long. = {lon} km vlim. = {v} km/h)".format( |
|||
v1=str(self.s1.num), |
|||
v2=str(self.s2.num), |
|||
lon=str(self.longueur), |
|||
v=str(self.v_moyenne) |
|||
) |
|||
|
|||
|
|||
def pointsAleatoires(n, L): |
|||
"""Crée un graphe de n points aléatoires non reliés dans le carré centré en |
|||
0 de largeure L. |
|||
|
|||
:param n: nombre de points. |
|||
:param L: Côté du carré. |
|||
""" |
|||
g = Graphe("Graphe aléatoire") |
|||
for _ in range(n): |
|||
x, y = random.uniform(-L/2, L/2), random.uniform(-L/2, L/2) |
|||
g.ajouteSommet(x, y) |
|||
return g |
|||
|
|||
|
|||
def test_gabriel(g, v1, v2): |
|||
"""Teste si on peut connecter deux sommets selon le critère de Gabriel. |
|||
|
|||
:param g: Le graphe. |
|||
:param v1: Premier sommet. |
|||
:param v2: Deuxième sommet. |
|||
""" |
|||
milieu = Sommet(((v1.x()+v2.x())/2, (v1.y()+v2.y())/2), -1) |
|||
rayon = v1.distance(v2)/2 |
|||
ajoute = True |
|||
for v3 in g.sommets: |
|||
if v3 in [v1, v2]: |
|||
continue |
|||
elif v3.distance(milieu) < rayon: |
|||
ajoute = False |
|||
break |
|||
return ajoute |
|||
|
|||
|
|||
def gabriel(g, ignore_gvr=False): |
|||
"""Crée le graphe de Gabriel associé à g avec des routes démartementales. |
|||
|
|||
:param g: Un graphe sans arrêtes. |
|||
:param ignore_gvr: booléen indiquant si on ne doit pas ajouter une arrête |
|||
quand gvr en ajoute une. |
|||
""" |
|||
for v1 in g.sommets: |
|||
for v2 in g.sommets: |
|||
if v2 == v1: |
|||
continue |
|||
if test_gabriel(g, v1, v2): |
|||
if (ignore_gvr and not test_gvr(g, v1, v2)) or not ignore_gvr: |
|||
g.ajouteDepartementale(v1, v2) |
|||
|
|||
|
|||
def test_gvr(g, v1, v2): |
|||
"""Teste si on peut connecter deux sommets selon le critère GVR. |
|||
|
|||
:param g: Le graphe. |
|||
:param v1: Premier sommet. |
|||
:param v2: Deuxième sommet. |
|||
""" |
|||
rayon = v1.distance(v2) |
|||
ajoute = True |
|||
for v3 in g.sommets: |
|||
if v3 in [v1, v2]: |
|||
continue |
|||
elif v3.distance(v1) < rayon and v3.distance(v2) < rayon: |
|||
ajoute = False |
|||
break |
|||
return ajoute |
|||
|
|||
|
|||
def gvr(g): |
|||
"""Crée le graphe GVR associé à g avec des routes nationales. |
|||
|
|||
:param g: Un graphe sans arrêtes. |
|||
""" |
|||
for v1 in g.sommets: |
|||
for v2 in g.sommets: |
|||
if v2 == v1: |
|||
continue |
|||
if test_gvr(g, v1, v2): |
|||
g.ajouteNationale(v1, v2) |
|||
|
|||
|
|||
def reseau(g): |
|||
"""Construit une carte routière avec comme nationales les routes obtenues |
|||
par la méthode GVR et départementales celles de Gabriel. |
|||
|
|||
:param g: Un raphe sans arêtes. |
|||
""" |
|||
gabriel(g, ignore_gvr=True) |
|||
gvr(g) |
|||
|
|||
|
|||
def delaunay(g): |
|||
"""Crée une triangulation de Delaunay à partir d ' un nuage de point""" |
|||
t = triangulation.Triangulation(g) |
|||
# Définit une fonction destinée à être appelée |
|||
# pour toute paire (s1,s2) de sommets |
|||
# qui forme une arête dans la triangulation de Delaunay. |
|||
# v3 et v4 sont les troisièmes sommets des deux triangles |
|||
# ayant (s1,s2) comme côté. |
|||
|
|||
def selectionneAretes(graphe, v1, v2, v3, v4): |
|||
# Fait de chaque arête de la triangulation |
|||
# une nationale |
|||
graphe.ajouteNationale(v1, v2) |
|||
# Construit le graphe de retour, égal à la triangulation de Delaunay |
|||
g = t.construitGrapheDeSortie(selectionneAretes) |
|||
g.renomme("Delaunay(" + str(g.n()) + ")") |
|||
return g |
|||
|
|||
|
|||
def reseauRapide(g): |
|||
"""Crée une triangulation de Delaunay à partir d ' un nuage de point""" |
|||
t = triangulation.Triangulation(g) |
|||
# Définit une fonction destinée à être appelée |
|||
# pour toute paire (s1,s2) de sommets |
|||
# qui forme une arête dans la triangulation de Delaunay. |
|||
# v3 et v4 sont les troisièmes sommets des deux triangles |
|||
# ayant (s1,s2) comme côté. |
|||
|
|||
def selectionneAretes(graphe, v1, v2, v3, v4): |
|||
if test_gabriel(graphe, v1, v2): |
|||
if test_gvr(graphe, v1, v2): |
|||
graphe.ajouteNationale(v1, v2) |
|||
else: |
|||
graphe.ajouteDepartementale(v1, v2) |
|||
# Construit le graphe de retour, égal à la triangulation de Delaunay |
|||
g = t.construitGrapheDeSortie(selectionneAretes) |
|||
g.renomme("Delaunay(" + str(g.n()) + ")") |
|||
return g |
@ -0,0 +1,195 @@ |
|||
# coding: utf-8 |
|||
|
|||
import sys |
|||
import numpy as np |
|||
import math |
|||
import copy |
|||
import PySide.QtCore as qtcore |
|||
import PySide.QtGui as qtgui |
|||
import traceback |
|||
|
|||
class Afficheur(qtgui.QWidget): |
|||
afficheurs = [] |
|||
size = (800, 600) |
|||
coord = np.array((50, 50)) |
|||
decalage = np.array((40,40)) |
|||
|
|||
def __init__(self, sujet, centre, ech): |
|||
super(Afficheur, self).__init__() |
|||
if(not "trace" in dir(sujet)): |
|||
raise Exception('Méthode "trace" non définie dans la classe ' + str(sujet.__class__)) |
|||
|
|||
app = qtgui.QApplication.instance() |
|||
if not app: |
|||
app = qtgui.QApplication(sys.argv) |
|||
desktop = app.desktop() |
|||
screen = desktop.screenGeometry(desktop.screenNumber(self)) |
|||
|
|||
self.rayon = 4 |
|||
self.ech = ech |
|||
self.centre = np.array(centre) |
|||
self.crayon = None |
|||
self.sujet = sujet |
|||
self.setStyleSheet("background-color: gray") |
|||
self.center = np.array(Afficheur.size) / 2. |
|||
self.setGeometry(Afficheur.coord[0] + screen.x(), Afficheur.coord[1] + screen.y(), Afficheur.size[0], Afficheur.size[1]) |
|||
Afficheur.coord += Afficheur.decalage |
|||
self.setWindowTitle("Afficheur") |
|||
self.show() |
|||
|
|||
def __pointVersPixel(self, p): |
|||
p = np.array(p) |
|||
pixel = (p - self.centre) * self.ech |
|||
pixel[1] = -pixel[1] |
|||
pixel += self.center |
|||
return qtcore.QPoint(pixel[0],pixel[1]) |
|||
|
|||
def __pixelVersPoint(self, pixel): |
|||
p = np.array((pixel.x(), pixel.y())) - self.center |
|||
p[1] = -p[1] |
|||
p = p / self.ech + self.centre |
|||
return p |
|||
|
|||
def __pixelVersVecteur(self, pixel1, pixel2): |
|||
assert isinstance(pixel1, qtcore.QPoint) |
|||
assert isinstance(pixel2, qtcore.QPoint) |
|||
v = np.array((pixel2.x(), pixel2.y())) - np.array((pixel1.x(), pixel1.y())) |
|||
v[1] = -v[1] |
|||
v = v / self.ech |
|||
return v |
|||
|
|||
def tracePoint(self, p): |
|||
assert self.crayon != None |
|||
assert isinstance(p, tuple) or isinstance(p, np.ndarray) |
|||
assert isinstance(p[0], float) |
|||
assert isinstance(p[1], float) |
|||
pixel = self.__pointVersPixel(p) |
|||
self.crayon.drawEllipse(pixel, self.rayon, self.rayon) |
|||
|
|||
def traceLigne(self, p1, p2): |
|||
assert self.crayon != None |
|||
assert isinstance(p1, tuple) or isinstance(p1, np.ndarray) |
|||
assert isinstance(p1[0], float) |
|||
assert isinstance(p1[1], float) |
|||
assert isinstance(p2, tuple) or isinstance(p2, np.ndarray) |
|||
assert isinstance(p2[0], float) |
|||
assert isinstance(p2[1], float) |
|||
pixel1 = self.__pointVersPixel(p1) |
|||
pixel2 = self.__pointVersPixel(p2) |
|||
self.crayon.drawLine(pixel1, pixel2) |
|||
|
|||
def changeCouleur(self, couleur): |
|||
assert self.crayon != None |
|||
assert isinstance(couleur, tuple) |
|||
assert isinstance(couleur[0], float) |
|||
assert isinstance(couleur[1], float) |
|||
assert isinstance(couleur[2], float) |
|||
c = qtgui.QColor(couleur[0]*255, couleur[1]*255, couleur[2]*255) |
|||
self.crayon.setPen(c) |
|||
self.crayon.setBrush(c) |
|||
|
|||
def traceTexte(self, p, texte): |
|||
assert isinstance(p, tuple) or isinstance(p, np.ndarray) |
|||
assert isinstance(p[0], float) |
|||
assert isinstance(p[1], float) |
|||
assert isinstance(texte, str) |
|||
pixel = self.__pointVersPixel(p) |
|||
pixel += qtcore.QPoint(10,-10) |
|||
self.crayon.drawText(pixel, texte) |
|||
|
|||
def renomme(self, titre): |
|||
# assert isinstance(titre, str) |
|||
# titre = unicode(titre, 'utf-8') |
|||
self.setWindowTitle(titre) |
|||
|
|||
def mousePressEvent(self, QMouseEvent): |
|||
pos = QMouseEvent.pos() |
|||
self.clic = pos |
|||
pos = self.__pixelVersPoint(pos) |
|||
self.centre = self.centre |
|||
|
|||
def mouseMoveEvent(self, QMouseEvent): |
|||
pos = QMouseEvent.pos() |
|||
self.centre -= self.__pixelVersVecteur(self.clic, pos) |
|||
self.clic = pos |
|||
self.update() |
|||
|
|||
def mouseReleaseEvent(self, QMouseEvent): |
|||
pos = QMouseEvent.pos() |
|||
self.centre -= self.__pixelVersVecteur(self.clic, pos) |
|||
self.clic = pos |
|||
self.update() |
|||
|
|||
def wheelEvent(self, event): |
|||
pos = event.pos() |
|||
C = self.__pixelVersPoint(pos) |
|||
k1 = self.ech |
|||
k2 = k1 * math.exp(0.001 * event.delta()) |
|||
self.centre = (self.centre * k1 + C * (k2 -k1)) / k2 |
|||
self.ech = k2 |
|||
self.update() |
|||
|
|||
def paintEvent(self, event): |
|||
if(self.crayon != None): |
|||
return |
|||
self.crayon = qtgui.QPainter(self) |
|||
self.trace() |
|||
self.crayon = None |
|||
|
|||
def trace(self): |
|||
self.crayon.setFont(qtgui.QFont('Decorative', 10)) |
|||
if(self.sujet != None): |
|||
try: |
|||
self.sujet.trace(self) |
|||
except AttributeError: |
|||
self.close() |
|||
qtgui.QApplication.quit() |
|||
raise Exception('Méthode "trace" non définie dans la classe ' + str(self.sujet.__class__)) |
|||
except BaseException: |
|||
traceback.print_exc() |
|||
self.close() |
|||
qtgui.QApplication.quit() |
|||
L = 100.; |
|||
x = 20; y = 20; |
|||
self.crayon.setPen('white') |
|||
msg = '{0:.2f} km'.format(L / self.ech) |
|||
l = self.crayon.fontMetrics().boundingRect(msg).width() |
|||
self.crayon.drawText(x + (L-l)/2, y-2, msg); |
|||
self.crayon.drawLine(x, y, x + L, y); |
|||
self.crayon.drawLine(x, y - L/20, x, y + L/10); |
|||
self.crayon.drawLine(x + L, y - L/20, x + L, y + L/10); |
|||
|
|||
|
|||
def sauvegarde(self): |
|||
filename = qtgui.QFileDialog.getSaveFileName(self, 'Fichier PDF') |
|||
if filename: |
|||
printer = qtgui.QPrinter(qtgui.QPrinter.HighResolution) |
|||
printer.setPageSize(qtgui.QPrinter.A4) |
|||
printer.setColorMode(qtgui.QPrinter.Color) |
|||
printer.setOutputFormat(qtgui.QPrinter.PdfFormat) |
|||
printer.setOutputFileName(filename) |
|||
|
|||
self.crayon = qtgui.QPainter(printer) |
|||
self.trace() |
|||
self.crayon = None |
|||
|
|||
def affiche(sujet, centre, ech, blocage = True): |
|||
'''Affiche dans une fenêtre l'objet sujet qui doit avoir une méthode trace''' |
|||
assert isinstance(centre, tuple) |
|||
assert isinstance(ech, float) |
|||
sujet = copy.deepcopy(sujet) |
|||
app = qtgui.QApplication.instance() |
|||
if not app: |
|||
app = qtgui.QApplication(sys.argv) |
|||
a = Afficheur(sujet, centre, ech) |
|||
Afficheur.afficheurs.append(a) |
|||
if(blocage): |
|||
bloque() |
|||
return a |
|||
|
|||
def bloque(): |
|||
app = qtgui.QApplication.instance() |
|||
if not app: |
|||
app = qtgui.QApplication(sys.argv) |
|||
app.exec_() |
|||
|
@ -0,0 +1,109 @@ |
|||
# coding: utf-8 |
|||
# La ligne précédente est indispensable pour accpeter les accents dans le fichier |
|||
|
|||
# Importe les modules qui seront nécessaires |
|||
import math # Fonctions mathématiques : math.cos, math.sin, math.pi, etc |
|||
import random # Générateurs de nombres aléatoires |
|||
import numpy # Calcul numérique sur des vecteurs, matrices, etc |
|||
|
|||
# Importe le fichier graphique.py qui |
|||
import graphique |
|||
|
|||
class Sommet: |
|||
'''Classe représentant un sommet d'un polygone''' |
|||
|
|||
def __init__(self, theta, r): |
|||
'''Constructeur de la classe Sommet : (theta,r) sont les coordonnées polaires du sommet self''' |
|||
self.deplace(theta, r) |
|||
|
|||
def deplace(self, theta, r): |
|||
'''Déplace le sommet self: (theta,r) sont les nouvelles coordonnées polaires''' |
|||
|
|||
# Sauvegarde les coordonnées polaires dans des attributs de l'objet self |
|||
self.theta = theta |
|||
self.r = r |
|||
|
|||
# Calcule les coordonnées cartésiennes (pour l'affichage) |
|||
# On utilise la bibliothèque de calcul numérique numpy. |
|||
self.pos = numpy.array([math.cos(theta), math.sin(theta)]) * r |
|||
|
|||
def __str__(self): |
|||
'''Fonction spéciale de conversion en chaîne de caractères''' |
|||
|
|||
chaine = '({:0.2f},{:0.2f})'.format(self.pos[0], self.pos[1]) |
|||
return chaine |
|||
|
|||
class Polygone: |
|||
''' Classe représentant un polygone du plan''' |
|||
|
|||
def __init__(self, n, R): |
|||
'''Constructeur d'un objet Polygone : crée un polygone régulier |
|||
à n sommets placés sur le cercle de rayon R centré sur l'origine''' |
|||
|
|||
# Vérifie que les arguments ont le type attendu |
|||
assert isinstance(n, int) |
|||
assert isinstance(R, float) |
|||
|
|||
# Crée comme attribut de self une liste pour accueillir les sommets |
|||
self.sommets = [] |
|||
|
|||
# Crée un attribut pour stocker le nom du polygone |
|||
self.nom = str(n) + "-polygone régulier" |
|||
|
|||
# Remplie la liste des n sommets |
|||
alpha = 2. * math.pi / n |
|||
angle = math.pi / 2. |
|||
for _ in range(n): |
|||
self.sommets.append(Sommet(angle, R)) |
|||
angle += alpha |
|||
|
|||
def __str__(self): |
|||
'''Fonction spéciale de conversion en chaîne de caractères''' |
|||
chaine = self.nom + ' = (' |
|||
if(len(self.sommets) > 0): |
|||
chaine += str(self.sommets[0]) |
|||
for s in self.sommets[1:]: |
|||
chaine += ', ' + str(s) |
|||
chaine += ')' |
|||
return chaine |
|||
|
|||
def secoue(self, amplitudeAngulaire, amplitudeRadiale): |
|||
'''Fonction qui déplace aléatoirement chaque sommet selon une amplitude angulaire et radiale réglable''' |
|||
|
|||
# Modifie les coordonnées de chaque sommet à l'aide de la méthode deplace |
|||
for s in self.sommets: |
|||
s.deplace( |
|||
s.theta + (random.random() * 2. - 1.) * amplitudeAngulaire, |
|||
s.r * (1. + (random.random() * 2. - 1.) * amplitudeRadiale) |
|||
) |
|||
|
|||
# Change le nom du polygone |
|||
self.nom = str(len(self.sommets)) + "-polygone secoué" |
|||
|
|||
|
|||
def trace(self, afficheur): |
|||
'''Fonction de dessin''' |
|||
|
|||
assert isinstance(afficheur, graphique.Afficheur) |
|||
|
|||
afficheur.renomme(self.nom) |
|||
precedent = self.sommets[-1] |
|||
afficheur.changeCouleur((0.,0.,0.)) |
|||
|
|||
for suivant in self.sommets: |
|||
afficheur.traceLigne(precedent.pos, suivant.pos) |
|||
precedent = suivant |
|||
|
|||
afficheur.changeCouleur((1.,0.,0.)) |
|||
for sommet in self.sommets: |
|||
afficheur.tracePoint(sommet.pos) |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
triangle = Polygone(3, 10.) |
|||
print(triangle) |
|||
graphique.affiche(triangle, (0., 0.), 10., blocage = False) |
|||
|
|||
heptagone = Polygone(7, 10.) |
|||
heptagone.secoue(math.pi / 5, 0.1) |
|||
graphique.affiche(heptagone, (0., 0.), 10.) |
@ -0,0 +1,136 @@ |
|||
# coding: utf-8 |
|||
|
|||
class Tas: |
|||
class Element: |
|||
def __init__(self, valeur, priorite, index): |
|||
self.valeur = valeur |
|||
self.priorite = priorite |
|||
self.index = index |
|||
|
|||
def __str__(self): |
|||
return str(self.valeur) |
|||
|
|||
def __init__(self, fonctionPriorite): |
|||
self.L = [] |
|||
self.fonctionPriorite = fonctionPriorite |
|||
|
|||
def __str__(self): |
|||
s = "[" |
|||
for elt in self.L: |
|||
s += " " + str(elt) |
|||
s += "]" |
|||
return s |
|||
|
|||
def __parent(self, elt): |
|||
if(elt.index == 0): |
|||
return None |
|||
else: |
|||
return self.L[(elt.index-1) // 2] |
|||
|
|||
def __fils_gauche(self, elt): |
|||
i = 2 * elt.index + 1 |
|||
if(i < len(self.L)): |
|||
return self.L[i] |
|||
else: |
|||
return None |
|||
|
|||
def __fils_droit(self, elt): |
|||
i = 2 * elt.index + 2 |
|||
if(i < len(self.L)): |
|||
return self.L[i] |
|||
else: |
|||
return None |
|||
|
|||
def __deplace(self, elt, index): |
|||
assert isinstance(elt, Tas.Element) |
|||
ancien = elt.index |
|||
self.L[index] = elt |
|||
elt.index = index |
|||
return ancien |
|||
|
|||
def __promeut(self, elt): |
|||
while(True): |
|||
parent = self.__parent(elt) |
|||
if(parent == None): |
|||
break |
|||
if(parent.priorite >= elt.priorite): |
|||
break |
|||
elt.index = self.__deplace(parent, elt.index) |
|||
self.__deplace(elt, elt.index) |
|||
|
|||
def actualise(self, elt): |
|||
assert isinstance(elt, Tas.Element) |
|||
elt.priorite = self.fonctionPriorite(elt.valeur) |
|||
self.__promeut(elt) |
|||
|
|||
def ajoute(self, valeur): |
|||
elt = Tas.Element(valeur, self.fonctionPriorite(valeur), len(self.L)) |
|||
self.L.append(elt) |
|||
self.__promeut(elt) |
|||
return elt |
|||
|
|||
def empty(self): |
|||
return len(self.L) == 0 |
|||
|
|||
def pop(self): |
|||
n = len(self.L) |
|||
if(n == 0): |
|||
return None |
|||
|
|||
tete = self.L[0] |
|||
elt = self.L[n - 1] |
|||
elt.index = 0 |
|||
|
|||
while True: |
|||
filsGauche = self.__fils_gauche(elt) |
|||
filsDroit = self.__fils_droit(elt) |
|||
plusPrioritaire = elt |
|||
if(filsGauche != None and filsGauche.priorite > plusPrioritaire.priorite): |
|||
plusPrioritaire = filsGauche |
|||
if(filsDroit != None and filsDroit.priorite > plusPrioritaire.priorite): |
|||
plusPrioritaire = filsDroit |
|||
elt.index = self.__deplace(plusPrioritaire, elt.index) |
|||
if(plusPrioritaire is elt): |
|||
break |
|||
self.L.pop() |
|||
return tete.valeur |
|||
|
|||
######################################### |
|||
# Exemple d'utilisatio de la classe Tas # |
|||
# Tri de tâches par ordre chronologique # |
|||
######################################### |
|||
|
|||
class Tache: |
|||
'''Tache devant être effectuée avant une date limite''' |
|||
def __init__(self, jour, nom): |
|||
self.jour = jour |
|||
self.nom = nom |
|||
# Cet attribut servira à accueillir la clé du tas |
|||
self.cle = None |
|||
|
|||
def __str__(self): |
|||
return self.nom + " ({})".format(self.jour) |
|||
|
|||
if __name__ == "__main__": |
|||
|
|||
# Le niveau de priorité d'un événement x est -x.jour : on trie donc les événements par ordre chronologique |
|||
def calculePriorite(tache): |
|||
return -tache.jour |
|||
|
|||
# On crée un tas dont les éléments sont des événements triés par ordre chronologique |
|||
T = Tas(calculePriorite) |
|||
|
|||
# Ajoute des événements au tas |
|||
evts = [ Tache(5, "T1"), Tache(10, "T2"), Tache(5, "T3"), Tache(3, "T4")] |
|||
for evt in evts: |
|||
evt.cle = T.ajoute(evt) |
|||
|
|||
# Supposons que la tache 2 devienne soudain assez urgente |
|||
evts[1].jour = 4 |
|||
T.actualise(evts[1].cle) |
|||
|
|||
# Retirons dans l'ordre chronologie les éléments du tas |
|||
while(not T.empty()): |
|||
print(T.pop()) |
|||
|
|||
|
@ -0,0 +1,172 @@ |
|||
import graphe |
|||
import graphique |
|||
import copy |
|||
import numpy as np |
|||
import matplotlib.pyplot as pl |
|||
import time |
|||
|
|||
|
|||
def creerGrapheFigure1(): |
|||
""" Crée le graphe de la figure 1 """ |
|||
g = graphe.Graphe("Graphe de la figure 1") |
|||
s1 = g.ajouteSommet(1.0, 1.0) |
|||
s2 = g.ajouteSommet(2.0, 3.5) |
|||
s3 = g.ajouteSommet(2.5, 2.5) |
|||
s4 = g.ajouteSommet(5.0, 2.0) |
|||
s1.couleur = (1., 1., 0.) |
|||
s2.couleur = (0., 0., 1.) |
|||
s3.couleur = (1., 0., 0.) |
|||
s4.couleur = (1., 1., 0.) |
|||
a = g.connecte(s1, s2, 4.0, 90.) |
|||
a.couleur = (1., 1., 0.) |
|||
g.connecte(s1, s4, 5.2, 124.) |
|||
g.connecte(s2, s3, 2.0, 54.) |
|||
g.connecte(s2, s4, 5.0, 90.) |
|||
return g |
|||
|
|||
|
|||
def testQuestion1_2(): |
|||
"""' Teste que la création d ' un graphe ne plante pas |
|||
print ”Question 1.2 :""" |
|||
creerGrapheFigure1() |
|||
print("Ok. Pas de plantage") |
|||
|
|||
|
|||
def testQuestion1_3(): |
|||
""" Teste l ' affichage d ' un graphe dans la console""" |
|||
print("Question 1.3 :") |
|||
g = creerGrapheFigure1() |
|||
print(g) |
|||
|
|||
|
|||
def testQuestion1_4(): |
|||
""" Teste du dessin d'un graphe.""" |
|||
print("Question 1.4:") |
|||
graphique.affiche(creerGrapheFigure1(), (3., 2.), 100.) |
|||
|
|||
|
|||
def testQuestion2_2(): |
|||
"""Teste de génération aléatoire de graphe.""" |
|||
print("Question 2.2:") |
|||
graphique.affiche(graphe.pointsAleatoires(5, 30), (0, 0), 10.) |
|||
|
|||
|
|||
def testQuestion2_4(): |
|||
"""Teste de gabriel et gvr.""" |
|||
print("Question 2.4") |
|||
g = graphe.pointsAleatoires(30, 30) |
|||
g.renomme("Gabriel") |
|||
g1 = copy.deepcopy(g) |
|||
g1.renomme("GVR") |
|||
graphe.gabriel(g) |
|||
graphe.gvr(g1) |
|||
graphique.affiche(g, (0, 0), 10.) |
|||
graphique.affiche(g1, (0, 0), 10.) |
|||
|
|||
|
|||
def testQuestion2_5(): |
|||
"""Teste de la création de réseau.""" |
|||
g = graphe.pointsAleatoires(30, 30) |
|||
graphe.reseau(g) |
|||
graphique.affiche(g, (0, 0), 10.) |
|||
|
|||
|
|||
def chronometre(fonctionTest, fonctionPreparation, parametres): |
|||
''' Mesure le temps d ' exécution fonctionTest pour différentes valeurs d ' |
|||
un paramètres ''' |
|||
temps = [] |
|||
# Pour chaque valeur de paramètre |
|||
for p in parametres: |
|||
# Génère les entrées du test pour la valeur p |
|||
entrees = fonctionPreparation(p) |
|||
# Lance le test pour ces entrées |
|||
print("t({}) = ".format(p), end="", flush=True) |
|||
debut = time.time() |
|||
fonctionTest(entrees) |
|||
fin = time.time() |
|||
# Mesure le temps d ' exécution |
|||
t = (fin - debut) |
|||
print("{:.2f} s".format(t)) |
|||
temps.append(t) |
|||
return temps |
|||
|
|||
def testQuestion2_6(): |
|||
"""Mesure la performance de graphe.reseau""" |
|||
prepare = lambda p : graphe.pointsAleatoires(p, 10) |
|||
valeurs_n = np.arange(1, 200) |
|||
temps = chronometre(graphe.reseau, prepare, valeurs_n) |
|||
pl.close('all') |
|||
pl.title("Mesure du temps d'exécution de `reseau`.") |
|||
pl.plot(valeurs_n, temps) |
|||
pl.xlabel("n") |
|||
pl.ylabel("temps") |
|||
pl.show() |
|||
pl.title("Mesure du temps d'exécution de `reseau`.") |
|||
pl.loglog(valeurs_n, temps, label='temps de calcul') |
|||
pl.loglog(valeurs_n, (lambda x : x**3)(valeurs_n), label='$x\mapsto x^3$') |
|||
pl.legend(loc='best') |
|||
pl.xlabel("n") |
|||
pl.ylabel("temps") |
|||
pl.show() |
|||
|
|||
def testQuestion2_7(): |
|||
"""Compare la création de graphe de gabriel et ed delaunay.""" |
|||
prepare = lambda p : graphe.pointsAleatoires(p, 10) |
|||
valeurs_n = np.arange(1, 200) |
|||
temps1 = chronometre(graphe.gabriel, prepare, valeurs_n) |
|||
temps2 = chronometre(graphe.delaunay, prepare, valeurs_n) |
|||
pl.close('all') |
|||
pl.title("Comparaison du temps pour `delaunay` et `gabriel`") |
|||
pl.plot(valeurs_n, temps1, label='gabriel') |
|||
pl.plot(valeurs_n, temps2, label='delaunay') |
|||
pl.legend(loc='best') |
|||
pl.xlabel('n') |
|||
pl.ylabel('temps') |
|||
pl.show() |
|||
|
|||
def testQuestion2_8(): |
|||
"""Compare le reseau naif et non naif.""" |
|||
g = graphe.pointsAleatoires(30, 30) |
|||
g1 = copy.deepcopy(g) |
|||
g.renomme("Naïf") |
|||
g1.renomme("Non naïf") |
|||
|
|||
graphe.reseau(g) |
|||
graphe.reseauRapide(g1) |
|||
graphique.affiche(g, (0, 0), 10.) |
|||
graphique.affiche(g1, (0, 0), 10.) |
|||
|
|||
def testQuestion2_9(): |
|||
"""Compare le temps de création des deux méthodes de création de réseau""" |
|||
prepare = lambda p : graphe.pointsAleatoires(p, 10) |
|||
valeurs_n = list(map(lambda x: int(x), np.logspace(1, 3, 30))) |
|||
temps1 = chronometre(graphe.reseau, prepare, valeurs_n) |
|||
temps2 = chronometre(graphe.reseauRapide, prepare, valeurs_n) |
|||
pl.close('all') |
|||
pl.title("Comparaison du temps d'exécution de `reseau` et `reseauRapide`.") |
|||
pl.plot(valeurs_n, temps1, label='reseau') |
|||
pl.plot(valeurs_n, temps2, label='reseauRapide') |
|||
pl.legend(loc='best') |
|||
pl.xlabel("n") |
|||
pl.ylabel("temps") |
|||
pl.show() |
|||
pl.title("Comparaison du temps d'exécution de `reseau` et `reseauRapide`.") |
|||
pl.loglog(valeurs_n, temps1, label='reseau') |
|||
pl.loglog(valeurs_n, temps2, label='reseauRapide') |
|||
pl.legend(loc='best') |
|||
pl.xlabel("n") |
|||
pl.ylabel("temps") |
|||
pl.show() |
|||
|
|||
|
|||
|
|||
# testQuestion1_2() |
|||
# testQuestion1_3() |
|||
# testQuestion1_4() |
|||
# testQuestion2_2() |
|||
# testQuestion2_4() |
|||
# testQuestion2_5() |
|||
# testQuestion2_6() |
|||
# testQuestion2_7() |
|||
# testQuestion2_8() |
|||
testQuestion2_9() |
@ -0,0 +1,299 @@ |
|||
# coding: utf-8 |
|||
|
|||
import sys |
|||
import math |
|||
|
|||
|
|||
def distanceAuCarre(v1, v2): |
|||
dx = (v1.x() - v2.x()) |
|||
dy = (v1.y() - v2.y()) |
|||
return dx**2 + dy**2 |
|||
|
|||
|
|||
def conditionDeDelaunay(A, B, C, D): |
|||
dab2 = distanceAuCarre(A, B) |
|||
dad2 = distanceAuCarre(A, D) |
|||
dbc2 = distanceAuCarre(B, C) |
|||
dbd2 = distanceAuCarre(B, D) |
|||
dcd2 = distanceAuCarre(C, D) |
|||
cosa = (dab2 + dad2 - dbd2) / (2 * math.sqrt(dab2) * math.sqrt(dad2)) |
|||
cosc = (dbc2 + dcd2 - dbd2) / (2 * math.sqrt(dbc2) * math.sqrt(dcd2)) |
|||
sina2 = 1. - cosa * cosa |
|||
if(sina2 > 0.): |
|||
sina = math.sqrt(sina2) |
|||
else: |
|||
sina = 0. |
|||
sinc2 = 1. - cosc * cosc |
|||
if(sinc2 > 0.): |
|||
sinc = math.sqrt(sinc2) |
|||
else: |
|||
sinc = 0. |
|||
return sina * cosc + cosa * sinc >= 0 |
|||
|
|||
|
|||
class Triangulation: |
|||
|
|||
class QuadEdge: |
|||
'''Classe définissant un quad-edge : une arête connectée à deux faces et deux sommets''' |
|||
|
|||
def __init__(self, f1, f2, v1, v2): |
|||
self.f1 = f1 |
|||
self.f2 = f2 |
|||
self.v1 = v1 |
|||
self.v2 = v2 |
|||
|
|||
def __eq__(self, e): |
|||
'''Méthode qui teste si deux quad-edges ont même extrémités''' |
|||
if((self.v1 == e.v1) and (self.v2 == e.v2)): |
|||
return True |
|||
elif((self.v1 == e.v2) and (self.v2 == e.v1)): |
|||
return True |
|||
else: |
|||
return False |
|||
|
|||
def __str__(self): |
|||
return str(self.v1) + " - " + str(self.v2) |
|||
|
|||
def remplaceFace(self, ancienneFace, nouvelleFace): |
|||
'''Méthode qui remplace une face adjacente au quad-edge par une autre''' |
|||
if(self.f1 == ancienneFace): |
|||
self.f1 = nouvelleFace |
|||
else: |
|||
self.f2 = nouvelleFace |
|||
|
|||
def lanceAlgorithme(self): |
|||
# Construit la triangulation initiale. Complexité en O(n log(n)) |
|||
self.construitTriangulationInitiale() |
|||
|
|||
# Bascule les arêtes de la triangulation jusqu'à obtenir la triangulation de Delaunay |
|||
self.basculeAretes() |
|||
|
|||
# Efface les sommets à l'infini |
|||
for _ in range(3): |
|||
self.graphe.sommets.pop() |
|||
|
|||
def __init__(self, g): |
|||
'''Constructeur de la classe triangulation qui prend en entrée un nuage de points |
|||
et crée une triangulation de Delaunay''' |
|||
self.racine = None |
|||
self.quadEdges = [] |
|||
self.graphe = g |
|||
self.lanceAlgorithme() |
|||
|
|||
class Triangle: |
|||
def __init__(self, v1, v2, v3): |
|||
self.v1 = v1 |
|||
self.v2 = v2 |
|||
self.v3 = v3 |
|||
self.t12 = None |
|||
self.t13 = None |
|||
self.t23 = None |
|||
self.e12 = None |
|||
self.e13 = None |
|||
self.e23 = None |
|||
|
|||
def contient(self, v): |
|||
''' Méthode qui teste si un sommet est à l'intérieur d'un triangle''' |
|||
v1 = self.v1 |
|||
v2 = self.v2 |
|||
v3 = self.v3 |
|||
det = (v1.x() - v3.x()) * (v2.y() - v3.y()) - \ |
|||
(v1.y() - v3.y()) * (v2.x() - v3.x()) |
|||
c1 = ((v2.y() - v3.y()) * (v.x() - v3.x()) + |
|||
(v3.x() - v2.x()) * (v.y() - v3.y())) * det |
|||
c2 = ((v3.y() - v1.y()) * (v.x() - v3.x()) + |
|||
(v1.x() - v3.x()) * (v.y() - v3.y())) * det |
|||
return (c1 > 0) and (c2 > 0) and (c1 + c2 < det * det) |
|||
|
|||
def trouve(self, v): |
|||
'''Méthode récursive qui renvoie le triangle d'une triangulation qui contient un sommet. |
|||
Renvoie le triangle contenant v, None si v n'est pas dans la triangulation''' |
|||
|
|||
t12 = self.t12 |
|||
t13 = self.t13 |
|||
t23 = self.t23 |
|||
if(t12 == None): |
|||
return self |
|||
else: |
|||
if(t12.contient(v)): |
|||
return t12.trouve(v) |
|||
elif(t13.contient(v)): |
|||
return t13.trouve(v) |
|||
elif(t23.contient(v)): |
|||
return t23.trouve(v) |
|||
else: |
|||
return None |
|||
|
|||
def aPourSommet(self, v): |
|||
'''Méthode qui teste si un sommet est un sommet d'un triangle |
|||
Renvoie vrai si v est un sommet du triangle''' |
|||
return self.v1 == v or self.v2 == v or self.v3 == v |
|||
|
|||
def troisiemeSommet(self, a, b): |
|||
''' Renvoie le troisième sommet d'un triangle à partir des deux autres''' |
|||
|
|||
if(self.v1 != a and self.v1 != b): |
|||
return self.v1 |
|||
elif(self.v2 != a and self.v2 != b): |
|||
return self.v2 |
|||
else: |
|||
return self.v3 |
|||
|
|||
def remplaceSommet(self, ancienSommet, nouveauSommet): |
|||
''' Méthode qui substitue un sommet par un autre dans un triangle ''' |
|||
if(self.v1 == ancienSommet): |
|||
self.v1 = nouveauSommet |
|||
elif(self.v2 == ancienSommet): |
|||
self.v2 = nouveauSommet |
|||
elif(self.v3 == ancienSommet): |
|||
self.v3 = nouveauSommet |
|||
else: |
|||
raise Exception("Bad vertex" + ancienSommet) |
|||
|
|||
def retrouveArete(self, v1, v2): |
|||
'''Méthode qui renvoie le quad-edge d'un triangle ayant deux sommets pour extrêmités''' |
|||
e = Triangulation.QuadEdge(None, None, v1, v2) |
|||
if(self.e12 == e): |
|||
return self.e12 |
|||
elif(self.e13 == e): |
|||
return self.e13 |
|||
elif(self.e23 == e): |
|||
return self.e23 |
|||
else: |
|||
raise Exception("getEdge") |
|||
|
|||
def remplaceArete(self, ancienneArete, nouvelleArete): |
|||
'''Méthode qui substitue le quad-edge d'un triangle par un autre''' |
|||
if(self.e12 == ancienneArete): |
|||
self.e12 = nouvelleArete |
|||
elif(self.e13 == ancienneArete): |
|||
self.e13 = nouvelleArete |
|||
elif(self.e23 == ancienneArete): |
|||
self.e23 = nouvelleArete |
|||
else: |
|||
raise Exception("changeEdge") |
|||
|
|||
def construitTriangleInitial(self): |
|||
'''Méthode qui construit une triangulation initiale qui contient tous les sommets''' |
|||
xmin = sys.float_info.max |
|||
xmax = sys.float_info.min |
|||
ymin = sys.float_info.max |
|||
ymax = sys.float_info.min |
|||
|
|||
for v in self.graphe.sommets: |
|||
if(v.x() > xmax): |
|||
xmax = v.x() |
|||
if(v.x() < xmin): |
|||
xmin = v.x() |
|||
if(v.y() > ymax): |
|||
ymax = v.y() |
|||
if(v.y() < ymin): |
|||
ymin = v.y() |
|||
|
|||
largeur = xmax - xmin |
|||
hauteur = ymax - ymin |
|||
margex = 0.1 * largeur |
|||
margey = 0.1 * hauteur |
|||
|
|||
v1 = self.graphe.ajouteSommet( |
|||
xmin - largeur / 2 - margex, ymin - margey) |
|||
v2 = self.graphe.ajouteSommet( |
|||
xmax + largeur / 2 + margex, ymin - margey) |
|||
v3 = self.graphe.ajouteSommet( |
|||
(xmin + xmax) / 2, ymax + hauteur + margey) |
|||
|
|||
self.racine = Triangulation.Triangle(v1, v2, v3) |
|||
|
|||
e12 = Triangulation.QuadEdge(None, self.racine, v1, v2) |
|||
e13 = Triangulation.QuadEdge(None, self.racine, v1, v3) |
|||
e23 = Triangulation.QuadEdge(None, self.racine, v2, v3) |
|||
self.racine.e12 = e12 |
|||
self.racine.e13 = e13 |
|||
self.racine.e23 = e23 |
|||
|
|||
def supprimeAretes(self): |
|||
for s in self.graphe.sommets: |
|||
s.aretes.clear() |
|||
|
|||
def construitTriangulationInitiale(self): |
|||
self.quadEdges = [] |
|||
self.construitTriangleInitial() |
|||
|
|||
for v in self.graphe.sommets: |
|||
t = self.racine.trouve(v) |
|||
if(t != None): |
|||
t.t12 = Triangulation.Triangle(t.v1, t.v2, v) |
|||
t.t13 = Triangulation.Triangle(t.v1, t.v3, v) |
|||
t.t23 = Triangulation.Triangle(t.v2, t.v3, v) |
|||
|
|||
e1v = Triangulation.QuadEdge(t.t12, t.t13, t.v1, v) |
|||
e2v = Triangulation.QuadEdge(t.t12, t.t23, t.v2, v) |
|||
e3v = Triangulation.QuadEdge(t.t13, t.t23, t.v3, v) |
|||
self.quadEdges.append(e1v) |
|||
self.quadEdges.append(e2v) |
|||
self.quadEdges.append(e3v) |
|||
|
|||
t.t12.e12 = t.e12 |
|||
t.e12.remplaceFace(t, t.t12) |
|||
t.t12.e13 = e1v |
|||
t.t12.e23 = e2v |
|||
|
|||
t.t13.e12 = t.e13 |
|||
t.e13.remplaceFace(t, t.t13) |
|||
t.t13.e13 = e1v |
|||
t.t13.e23 = e3v |
|||
|
|||
t.t23.e12 = t.e23 |
|||
t.e23.remplaceFace(t, t.t23) |
|||
t.t23.e13 = e2v |
|||
t.t23.e23 = e3v |
|||
|
|||
def basculeAretes(self): |
|||
pile = [] |
|||
for qe in self.quadEdges: |
|||
pile.append(qe) |
|||
|
|||
while(len(pile) > 0): |
|||
e = pile.pop() |
|||
|
|||
f1 = e.f1 |
|||
f2 = e.f2 |
|||
if(f1 != None and f2 != None): |
|||
v1 = e.v1 |
|||
v2 = e.v2 |
|||
|
|||
v3 = f1.troisiemeSommet(v1, v2) |
|||
v4 = f2.troisiemeSommet(v1, v2) |
|||
|
|||
if(not conditionDeDelaunay(v3, v1, v4, v2)): |
|||
v1v3 = f1.retrouveArete(v1, v3) |
|||
v2v3 = f1.retrouveArete(v2, v3) |
|||
v1v4 = f2.retrouveArete(v1, v4) |
|||
v2v4 = f2.retrouveArete(v2, v4) |
|||
f1.remplaceArete(v2v3, v1v4) |
|||
f2.remplaceArete(v1v4, v2v3) |
|||
v2v3.remplaceFace(f1, f2) |
|||
v1v4.remplaceFace(f2, f1) |
|||
f1.remplaceSommet(v2, v4) |
|||
f2.remplaceSommet(v1, v3) |
|||
e.v1 = v3 |
|||
e.v2 = v4 |
|||
pile.append(v1v3) |
|||
pile.append(v2v3) |
|||
pile.append(v1v4) |
|||
pile.append(v2v4) |
|||
|
|||
def construitGrapheDeSortie(self, selectionneArete): |
|||
'''Méthode qui construit la triangulation de Delaunay à partir du nuage de points passé au constructeur''' |
|||
|
|||
# Sélectionne les arêtes |
|||
graphe = self.graphe |
|||
racine = self.racine |
|||
for qe in self.quadEdges: |
|||
v1 = qe.v1 |
|||
v2 = qe.v2 |
|||
if(not (racine.aPourSommet(v1) or racine.aPourSommet(v2))): |
|||
v3 = qe.f1.troisiemeSommet(v1, v2) |
|||
v4 = qe.f2.troisiemeSommet(v1, v2) |
|||
selectionneArete(graphe, v1, v2, v3, v4) |
|||
return self.graphe |
Loading…
Reference in new issue