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