Algorithme de tracé de segment de bresenham

Algorithme de tracé de segment de bresenham

Algorithme de tracé de segment de Bresenham

Illustration de l'algorithme de Bresenham

Lalgorithme de tracé de segment de Bresenham est un algorithme développé par Bresenham en mai 1962, alors quil travaillait dans un laboratoire informatique dIBM et cherchait à piloter un traceur attaché à une console texte. Cet algorithme a été présenté à la convention de lACM en 1963, puis publié en 1965 dans la revue IBM Systems Journal.

Lalgorithme détermine quels sont les points dun plan discret qui doivent être tracés afin de former une approximation de segment de droite entre deux points donnés. Cet algorithme est souvent utilisé pour dessiner des segments de droites sur lécran dun ordinateur ou une image calculée pour limpression. Il est considéré comme lun des premiers algorithmes découverts dans le domaine de la synthèse d'image.

Sommaire

Utilisations

Le principe du calcul est largement documenté et a depuis été repris pour tracer de façon incrémentale nimporte quelle courbe conique (cercle, ellipse, arc, parabole, hyperbole) ou courbes de Bézier grâce aux propriétés de leur fonction polynomiale de définition, dont les dérivées permettent de calculer les orientations de segments élémentaires avec de simples opérations entières. On peut même ladapter à lapproximation de courbes dont on ne connaît quun développement limité (dont on ne prendra que les premiers termes de faible degré), utilisable avec une bonne précision sur un domaine suffisant par rapport à la résolution (sinusoïdes, exponentielles, puissances non entières).

Lalgorithme est également facilement adaptable au calcul de courbes et surfaces dans un espace discret de plus de 2 dimensions (notamment pour le pilotage de machines outils). Même en deux dimensions seulement, on peut discrétiser avec cet algorithme une courbe avec une fonction de lissage prenant en compte lerreur commise entre deux points candidats afin dajuster leur couleur, lerreur incrémentale étant convertible en coefficient de transparence, ce qui permet de conserver la graisse (épaisseur visuelle) dune courbe tout en limitant leffet descalier (crénelage).

Cet algorithme intervient aussi dans le lissage de rendus de textures 2D appliquées sur des surfaces dune scène 3D la texture subit des réductions ou agrandissements. On lemploie aussi pour le lissage dagrandissements photographiques, ou pour linterpolation de couleurs intermédiaires sur une échelle discrète.

Explication de lalgorithme de base dans le premier octant

Illustration du résultat de lalgorithme de tracé de segment de Bresenham.

La ligne est tracée entre deux points (x1, y1) et (x2, y2), chaque paire indique la colonne et la rangée, respectivement, croissant vers le bas et la droite. Nous supposerons au départ que notre segment descend vers le bas et la droite, et que la distance horizontale x2-x1 excède la distance verticale y2-y1 (cest-à-dire que le segment a une pente inférieure ou égale à 1). Notre but est, pour chaque colonne x entre x0 et x1, didentifier la rangée y dans cette colonne qui est la plus proche du segment idéal et de tracer un pixel en (x, y).

Détermination des ordonnées

Maintenant, comment pouvons-nous déterminer quel pixel est le plus proche de la droite pour une colonne donnée ? La formule générale dune droite entre les deux points est donnée par :

y - y_1 = \frac{y_2 - y_1}{x_2 -x_1} \cdot \left(x - x_1\right).   (1)

Puisque nous connaissons la colonne \dot{x}, la rangée \dot{y} du pixel le plus proche de lordonnée exacte du point dabscisse x = \dot{x} sur la droite est donnée en arrondissant cette ordonnée y\ à lentier le plus proche :

\dot{y} = \left\lfloor \frac{y_2 - y_1}{x_2 - x_1} \cdot \left(\dot{x} - x_1\right) + y_1 + 0,5 \right\rfloor.   (2)

Cependant, le calcul explicite de cette valeur pour chaque colonne \dot{x} est coûteux. Or \left(\dot{x}, \dot{y}\right) commence en (x_1, y_1)\ , et que chaque fois que nous ajoutons 1 à l'abscisse, nous ajoutons la valeur constante e_{(1,0)} = \frac{y_2 - y_1}{x_2 - x_1} à la valeur de lordonnée y\ du point de la droite correspondant. Cette valeur est la pente de la droite, et en vertu de notre hypothèse initiale, elle est comprise entre 0 et 1. Après larrondi, pour chaque colonne \dot{x}, nous utilisons donc soit la valeur \dot{y} précédente (ordonnée du pixel d'abscisse \dot{x}-1), soit cette valeur augmentée de 1.

On peut décider laquelle de ces deux valeurs prendre en conservant une valeur derreur qui représente la distance verticale entre la valeur \dot{y} courante et la valeur y exacte pour la droite à labscisse x = \dot{x} courante. Au départ cette valeur derreur e est nulle et chaque fois que nous incrémentons \dot{x}, nous augmentons la valeur derreur par la valeur de pente ci-dessus. Chaque fois que lerreur dépasse 0,5, la droite est devenue plus proche de la valeur \dot{y} suivante, aussi nous ajouterons 1 à \dot{y} en retranchant simultanément 1,0 à lerreur e.

La procédure ressemble à ceci, en supposant que tracerPixel(x, y) est une primitive graphique traçant le pixel de rangée x et de colonne y ; exprimé en pseudo-code, lalgorithme de base est :

procédure tracerSegment(entier x1, entier y1, entier x2, entier y2) est
  déclarer entier x, y, dx, dy ;
  déclarer rationnel e, e(1,0), e(0,1) ;  // valeur derreur et incréments
  dyy2 - y1 ;
  dxx2 - x1 ;
  yy1 ;  // rangée initiale
  e0,0 ;  // valeur derreur initiale
  e(1,0)dy / dx ;
  e(0,1) ← -1.0 ;
  pour x variant de x1 jusquà x2 par incrément de 1 faire
    tracerPixel(x, y) ;
    si (ee + e(1,0))  0,5 alors  // erreur pour le pixel suivant de même rangée
      yy + 1 ;  // choisir plutôt le pixel suivant dans la rangée supérieure
      ee + e(0,1) ;  // ajuste lerreur commise dans cette nouvelle rangée
    fin si ;
  fin pour ;
fin procédure ;

Amélioration de lalgorithme pour le calcul avec des entiers

Le problème avec cet algorithme simple est que les microprocesseurs dordinateur sont relativement lents dans le calcul sur des nombres en virgule flottante (et la représentation suggérée ci-dessus sous forme de nombres rationnels pour e et e(1,0) est nettement plus complexe et non nativement prise en charge par les processeurs ce qui augmente le nombre dinstructions pour travailler sur de tels nombres) ; de plus, les erreurs dapproximation du nombre flottant e(1,0) saccumulent à chaque addition de e(1,0) dans e. Travailler avec uniquement des entiers permettrait un calcul à la fois plus exact et plus rapide.

La première astuce est de remarquer dabord quon peut remplacer e par e-0,5, ce qui permet de ne tester que le signe de la valeur derreur au lieu de comparer deux rationnels, le test de proximité par rapport à la droite exacte revient alors à savoir lequel des deux pixels candidats se situe en dessous de la droite exacte parallèle dont les ordonnées sont augmentées de 0,5, et de remplacer larrondi de la formule (1) ci-dessus dans à lentier le plus proche par un arrondi à lentier égal ou inférieur avec cette nouvelle droite, ce qui ne change effectivement pas la formule (2) ci-dessus, mais e représentera lerreur commise lors de l'approximation de cette seconde droite par les pixels tracés :

procédure tracerSegment(entier x1, entier y1, entier x2, entier y2) est
  déclarer entier x, y, dx, dy ;
  déclarer rationnel e, e(1,0), e(0,1) ;  // valeur derreur et incréments
  dyy2 - y1 ;
  dxx2 - x1 ;
  yy1 ;  // rangée initiale
  e ← -0,5 ;  // valeur derreur initiale
  e(1,0)dy / dx ;
  e(0,1) ← -1.0 ;
  pour x variant de x1 jusquà x2 par incrément de 1 faire
    tracerPixel(x, y) ;
    si (ee + e(1,0))  0 alors  // erreur pour le pixel suivant de même rangée
      yy + 1 ;  // choisir plutôt le pixel suivant dans la rangée supérieure
      ee + e(0,1) ;  // ajuste lerreur commise dans cette nouvelle rangée
    fin si ;
  fin pour ;
fin procédure ;

Ensuite en multipliant tous les rationnels ci-dessus par dx, le calcul ne demande plus dincréments rationnels (ce qui élimine l'accumulation d'erreurs dapproximation des flottants). Cependant la valeur initiale de e=-0,5×dx nest pas encore entière, même si ses incréments sont entiers.

procédure tracerSegment(entier x1, entier y1, entier x2, entier y2) est
  déclarer entier x, y, dx, dy ;
  déclarer rationnel e ; // valeur derreur
  déclarer entier e(1,0), e(0,1) ;  // incréments
  dyy2 - y1 ;
  dxx2 - x1 ;
  yy1 ;  // rangée initiale
  e ← -0,5 × dx ;  // valeur derreur initiale
  e(1,0)dy ;
  e(0,1) ← -dx ;
  pour x variant de x1 jusquà x2 par incrément de 1 faire
    tracerPixel(x, y) ;
    si (ee + e(1,0))  0 alors  // erreur pour le pixel suivant de même rangée
      yy + 1 ;  // choisir plutôt le pixel suivant dans la rangée supérieure
      ee + e(0,1) ;  // ajuste lerreur commise dans cette nouvelle rangée
    fin si ;
  fin pour ;
fin procédure ;

Toutefois en doublant e (et les valeurs de ses incréments), cela ne change rien au test de son signe : e représentera alors la distance par rapport à la droite exacte dordonnées augmentées de 0,5, cette distance étant multipliée par le facteur constant positif 2×dy (ce qui ne change pas le signe de la valeur derreur testée). La valeur de pente utilisée comme incrément de e étant aussi multipliée par le même facteur devient simplement 2×dy, sa valeur initiale devient -dx, le second décrément conditionnel devient 2×dx (que lon peut aussi précalculer). Lalgorithme devient alors :

procédure tracerSegment(entier x1, entier y1, entier x2, entier y2) est
  déclarer entier x, y, dx, dy ;
  déclarer entier e ; // valeur derreur
  déclarer entier e(1,0), e(0,1) ;  // incréments
  dyy2 - y1 ;
  dxx2 - x1 ;
  yy1 ;  // rangée initiale
  e ← -dx ;  // valeur derreur initiale
  e(1,0)dy × 2 ;
  e(0,1) ← -dx × 2;
  pour x variant de x1 jusquà x2 par incrément  de 1 faire
    tracerPixel(x, y) ;
    si (ee + e(1,0))  0 alors  // erreur pour le pixel suivant de même rangée
      yy + 1 ;  // choisir plutôt le pixel suivant dans la rangée supérieure
      ee + e(0,1) ;  // ajuste lerreur commise dans cette nouvelle rangée
    fin si ;
  fin pour ;
fin procédure ;

Réduction des variables et simplifications

On pourra enfin changer le signe de e en testant le signe opposé, puis réduire le nombre de variables, en constatant que dx et dy ci-dessus ne sont plus utilisés dès que lerreur initiale et les incréments sont calculés ; il suffit de changer aussi le signe des incréments et décréments :

procédure tracerSegment(entier x1, entier y1, entier x2, entier y2) est
  déclarer entier dx, dy ;
  déclarer entier e ; // valeur derreur
  ex2 - x1 ;        // -e(0,1)
  dxe × 2 ;          // -e(0,1)
  dy ← (y2 - y1) × 2 ;  // e(1,0)
  tant que x1 < x2 faire
    tracerPixel(x1, y1) ;
    x1x1 + 1 ;  // colonne du pixel suivant
    si (ee - dy)  0 alors  // erreur pour le pixel suivant de même rangée
      y1y1 + 1 ;  // choisir plutôt le pixel suivant dans la rangée supérieure
      ee + dx ;  // ajuste lerreur commise dans cette nouvelle rangée
    fin si ;
  fin faire ;
  // Le pixel final (x2, y2) nest pas tracé.
fin procédure ;

Cet algorithme est optimal et suffisant pour tracer tout vecteur horizontal diagonal ou oblique dans le premier octant, de colonnes et rangées croissantes. Si le langage de programmation le permet, les deux variables locales déclarées x et y peuvent être remplacées par réutilisation des variables x1 et y1 des paramètres. Ce cas est traité ci-dessus.

Toutefois il faut remarquer dans lalgorithme ci-dessus que le test du signe de e peut aussi bien inclure légalité avec zéro ou ne pas linclure. Cela correspond au fait que les deux pixels suivants candidats sont équidistants de la droite exacte. Si on choisit un déplacement diagonal le deuxième point suivant sera toujours obtenu par un déplacement horizontal ; si on choisit un déplacement horizontal le deuxième point suivant sera toujours obtenu par un déplacement diagonal. Si on inversait la direction du vecteur, les pixels choisis seraient inversés donc différents, et on devra en tenir compte si on souhaite un recouvrement exact des pixels de deux vecteurs obliques de sens opposés, lors de la généralisation de lalgorithme à des vecteurs obliques de directions quelconques (ce cas ne peut pas se produire pour le tracé de vecteurs horizontaux, verticaux ou diagonaux).

Algorithme général optimisé

La généralisation de lalgorithme de base au tracé de vecteurs de direction quelconque est obtenue par simple symétries.

Lalgorithme est ici développé et optimisé dans chacun des huit octants. Toutefois, afin de sassurer que les mêmes pixels seront toujours tracés pour deux vecteurs identiques mais de direction opposée, on inversera les cas limites un déplacement diagonal est à égalité avec un déplacement droit, en choissant la diagonale quand le vecteur est orienté vers la gauche (abscisses décroissantes) plutôt que vers la droite (abscisses croissantes) comme dans le cas simplifié ci-dessus :

procédure tracerSegment(entier x1, entier y1, entier x2, entier y2) est
  déclarer entier dx, dy;
  
  si (dxx2 - x1) ≠ 0 alors
    si dx > 0 alors
      si (dyy2 - y1) ≠ 0 alors
        si dy > 0 alors
          // vecteur oblique dans le 1er quadran
          
          si dx  dy alors
            // vecteur diagonal ou oblique proche de lhorizontale, dans le 1er octant
            déclarer entier e ;
            dx ← (edx) × 2 ; dydy × 2 ;  // e est positif
            boucler sans fin  // déplacements horizontaux
              tracePixel(x1, y1) ;
              interrompre boucle si (x1x1 + 1) = x2 ;
              si (ee - dy) < 0 alors
                y1y1 + 1 ;  // déplacement diagonal
                ee + dx ;
              fin si ;
            fin boucle ;
          sinon
            // vecteur oblique proche de la verticale, dans le 2nd octant
            déclarer entier e ;
            dy ← (edy) × 2 ; dxdx × 2 ;  // e est positif
            boucler sans fin  // déplacements verticaux
              tracePixel(x1, y1) ;
              interrompre boucle si (y1y1 + 1) = y2 ;
              si (ee - dx) < 0 alors
                x1x1 + 1 ;  // déplacement diagonal
                ee + dy ;
              fin si ;
            fin boucle ;
          fin si ;
          
        sinon  // dy < 0 (et dx > 0)
          // vecteur oblique dans le 4e cadran
          
          si dx  -dy alors
            // vecteur diagonal ou oblique proche de lhorizontale, dans le 8e octant
            déclarer entier e ;
            dx ← (edx) × 2 ; dydy × 2 ;  // e est positif
            boucler sans fin  // déplacements horizontaux
              tracePixel(x1, y1) ;
              interrompre boucle si (x1x1 + 1) = x2 ;
              si (ee + dy) < 0 alors
                y1y1 - 1 ;  // déplacement diagonal
                ee + dx ;
              fin si ;
            fin boucle ;
          sinon  // vecteur oblique proche de la verticale, dans le 7e octant
            déclarer entier e ;
            dy ← (edy) × 2 ; dxdx × 2 ;  // e est négatif
            boucler sans fin  // déplacements verticaux
              tracePixel(x1, y1) ;
              interrompre boucle si (y1y1 - 1) = y2 ;
              si (ee + dx) > 0 alors
                x1x1 + 1 ;  // déplacement diagonal
                ee + dy ;
              fin si ;
            fin boucle ;
          fin si ;
          
        fin si ;
      sinon  // dy = 0 (et dx > 0)
        
        // vecteur horizontal vers la droite
        répéter
          tracePixel(x1, y1) ;
        jusquà ce que (x1x1 + 1) = x2 ;
        
      fin si ;
    sinon  // dx < 0
      si (dyy2 - y1) ≠ 0 alors
        si dy > 0 alors
          // vecteur oblique dans le 2nd quadran
          
          si -dx  dy alors
            // vecteur diagonal ou oblique proche de lhorizontale, dans le 4e octant
            déclarer entier e ;
            dx ← (edx) × 2 ; dydy × 2 ;  // e est négatif
            boucler sans fin  // déplacements horizontaux
              tracePixel(x1, y1) ;
              interrompre boucle si (x1x1 - 1) = x2 ;
              si (ee + dy)  0 alors
                y1y1 + 1 ;  // déplacement diagonal
                ee + dx ;
              fin si ;
            fin boucle ;
          sinon
            // vecteur oblique proche de la verticale, dans le 3e octant
            déclarer entier e ;
            dy ← (edy) × 2 ; dxdx × 2 ;  // e est positif
            boucler sans fin  // déplacements verticaux
              tracePixel(x1, y1) ;
              interrompre boucle si (y1y1 + 1) = y2 ;
              si (ee + dx)  0 alors
                x1x1 - 1 ;  // déplacement diagonal
                ee + dy ;
              fin si ;
            fin boucle ;
          fin si ;
          
        sinon  // dy < 0 (et dx < 0)
          // vecteur oblique dans le 3e cadran
          
          si dx  dy alors
            // vecteur diagonal ou oblique proche de lhorizontale, dans le 5e octant
            déclarer entier e ;
            dx ← (edx) × 2 ; dydy × 2 ;  // e est négatif
            boucler sans fin  // déplacements horizontaux
              tracePixel(x1, y1) ;
              interrompre boucle si (x1x1 - 1) = x2 ;
              si (ee - dy)  0 alors
                y1y1 - 1 ;  // déplacement diagonal
                ee + dx ;
              fin si ;
            fin boucle ;
          sinon  // vecteur oblique proche de la verticale, dans le 6e octant
            déclarer entier e ;
            dy ← (edy) × 2 ; dxdx × 2 ;  // e est négatif
            boucler sans fin  // déplacements verticaux
              tracePixel(x1, y1) ;
              interrompre boucle si (y1y1 - 1) = y2 ;
              si (ee - dx)  0 alors
                x1x1 - 1 ;  // déplacement diagonal
                ee + dy ;
              fin si ;
            fin boucle ;
          fin si ;
          
        fin si ;
      sinon  // dy = 0 (et dx < 0)
        
        // vecteur horizontal vers la gauche
        répéter
          tracePixel(x1, y1) ;
        jusquà ce que (x1x1 - 1) = x2 ;
        
      fin si ;
    fin si ;
  sinon  // dx = 0
    si (dyy2 - y1) ≠ 0 alors
      si dy > 0 alors
        
        // vecteur vertical croissant
        répéter
          tracePixel(x1, y1) ;
        jusquà ce que (y1y1 + 1) = y2 ;
        
      sinon  // dy < 0 (et dx = 0)
        
        // vecteur vertical décroissant
        répéter
          tracePixel(x1, y1) ;
        jusquà ce que (y1y1 - 1) = y2 ;
        
      fin si ;
    fin si ;
  fin si ;
  // le pixel final (x2, y2) nest pas tracé.
fin procédure ;

Notes :

  • Ci-dessus, les assignations sont parfois regroupées au sein des expressions qui en réutilisent la valeur assignée. Si le langage utilisé ne le permet pas, il suffit d'effectuer les assignations internes dans des instructions dassignation préalables séparées, et de relire cette variable dans lexpression contenante.
  • Les primitives graphiques tracePixel(x1, y1) sont regroupées ci-dessus pour tracer dans la boucle interne des segments horizontaux (cas du premier octant ci-dessus) ou verticaux (second octant), et peuvent être regroupées en un seul appel (il suffit de compter le nombre de passages dans la boucle interne, et deffectuer le tracé de segment horizontal (ou vertical) en sortie de cette boucle.
  • Portail de l’informatique Portail de linformatique
Ce document provient de « Algorithme de trac%C3%A9 de segment de Bresenham ».

Wikimedia Foundation. 2010.

Contenu soumis à la licence CC-BY-SA. Source : Article Algorithme de tracé de segment de bresenham de Wikipédia en français (auteurs)

Игры ⚽ Поможем написать курсовую

Regardez d'autres dictionnaires:

  • Algorithme De Tracé De Segment De Bresenham — L’algorithme de tracé de segment de Bresenham est un algorithme développé par Bresenham en mai 1962, alors qu’il travaillait dans un laboratoire informatique d’IBM et cherchait à piloter un traceur attaché à une console texte. Cet algorithme a… …   Wikipédia en Français

  • Algorithme de trace de segment de Bresenham — Algorithme de tracé de segment de Bresenham L’algorithme de tracé de segment de Bresenham est un algorithme développé par Bresenham en mai 1962, alors qu’il travaillait dans un laboratoire informatique d’IBM et cherchait à piloter un traceur… …   Wikipédia en Français

  • Algorithme de tracé de segment de Bresenham — L’algorithme de tracé de segment de Bresenham est un algorithme développé par Bresenham en mai 1962, alors qu’il travaillait dans un laboratoire informatique d’IBM et cherchait à piloter un traceur attaché à une console texte. Cet algorithme a… …   Wikipédia en Français

  • Algorithme de tracé de segment de Xiaolin Wu — L algorithme de tracé de segment de Xiaolin Wu est un algorithme permettant de tracer des courbes non crénelées qui a été présenté dans l article An Efficient Antialiasing Technique de Juillet 1991 issue de Computer Graphics ainsi que dans l… …   Wikipédia en Français

  • Algorithme de tracé de segment fenêtré de Hadrien Flammang — L algorithme de tracé de segment fenêtré de Hadrien Flammang permet de ne tracer d un segment que la partie qui est visible dans une zone rectangulaire (fenêtre). Il est basé sur l algorithme de Bresenham. On veut tracer un segment du point de… …   Wikipédia en Français

  • Algorithme De Tracé De Segment — Un algorithme de tracé de segment est un algorithme utilisé en infographie pour tracer approximativement un segment de droite sur des média graphiques discrets. Sur les média discrets, tels que les écrans ou les imprimantes avec une définition… …   Wikipédia en Français

  • Algorithme de trace de segment — Algorithme de tracé de segment Un algorithme de tracé de segment est un algorithme utilisé en infographie pour tracer approximativement un segment de droite sur des média graphiques discrets. Sur les média discrets, tels que les écrans ou les… …   Wikipédia en Français

  • Algorithme de tracé de segment — Un algorithme de tracé de segment est un algorithme utilisé en infographie pour tracer approximativement un segment de droite sur des média graphiques discrets. Sur les média discrets, tels que les écrans ou les imprimantes avec une définition… …   Wikipédia en Français

  • Algorithme De Tracé D'arc De Cercle De Bresenham — L’algorithme de tracé d arc de cercle de Bresenham, ou algorithme de tracé d arc de cercle par point milieu (midpoint en anglais) permet, pour une complexité algorithmique très réduite, de tracer des cercles en image matricielle. Sommaire 1… …   Wikipédia en Français

  • Algorithme de trace d'arc de cercle de Bresenham — Algorithme de tracé d arc de cercle de Bresenham L’algorithme de tracé d arc de cercle de Bresenham, ou algorithme de tracé d arc de cercle par point milieu (midpoint en anglais) permet, pour une complexité algorithmique très réduite, de tracer… …   Wikipédia en Français

Share the article and excerpts

Direct link
https://fr-academic.com/dic.nsf/frwiki/81685 Do a right-click on the link above
and select “Copy Link”