// Auteurs
// Matthieu Aubry
// Anthony Combe
// Licence
// Domaine publique
unit comptage_unit;

INTERFACE
// UNITES
uses image_unit, outils_unit, mesure_unit;

// TYPES
type
    // objet 
    comp_objet = record
        x0 : integer;
        y0 : integer;
        x1 : integer;
        y1 : integer;
        x : integer;
        y : integer;
        surface : integer;
        perimetre : integer
        end;

    // pointeur vers l'objet
    pcomp_objet = ^comp_objet;
    
    // pointeur vers la liste d'objets
    pliste_objets = ^Tliste_objets;
    
    // liste cha�� d'objets
    Tliste_objets = record
        objet : comp_objet;
        psuivant : pliste_objets
        end;
    
    // diff�entes mesures calculables
    mesures = (hauteur, largeur, surface, perimetre, rapport_surface_perimetre, rapport_hauteur_largeur, rapport_taille_surface) ;

// FONCTIONS
function init_liste():pliste_objets;
function is_empty(pdeb:pliste_objets):boolean;
function get_nb_objets(pdeb : pliste_objets):integer;
function get_objet(pdeb : pliste_objets ; k : integer):pcomp_objet;
function get_id_objet(pdeb : pliste_objets ; pobj : pcomp_objet):integer;
function add_rect_objets(img : pTimage; pdeb : pliste_objets):pTimage;

// PROCEDURES
procedure print_infos_objet(pdeb : pliste_objets; objet : pcomp_objet);
procedure delete_objet(var pdebx : pliste_objets ; k : integer);
procedure add_objet(var pdeb : pliste_objets; x,y : integer);
procedure remplir_objet(x,y,k : integer; img: pTimage; pdeb : pliste_objets);
procedure capture_objet(img : pTimage; var pdeb : pliste_objets);
procedure supprime_objets_hors_gabarit(img : pTimage; var pdebx : pliste_objets; mesure : mesures; min, max : real);
procedure print_all_infos_objets(pdeb : pliste_objets);

// MESURE
function lire_mesure_objet(objet : pcomp_objet; mesure : mesures):real;

IMPLEMENTATION

//
// initialise une liste d'objets
// @return renvoie le pointeur nil
function init_liste():pliste_objets;
begin
    init_liste := nil;
end;

//
// teste si la liste est vide ou non
// @return boolean liste vide ou non
function is_empty(pdeb:pliste_objets):boolean;
begin
    is_empty:=(pdeb=Nil);
end;

//
// renvoie le nombre d'objets dans la liste
// @param pdeb : pointeur de d�ut de liste
// @return nombre d'objets dans la liste
function get_nb_objets(pdeb : pliste_objets):integer;
var pl : pliste_objets;
    i : integer;
begin
    pl := pdeb;
    i:=0;
    
    if(pl=nil) then writeln('La liste est vide.');
    
    while (not is_empty(pl)) do
    begin
        pl := pl^.psuivant;
        i := i + 1;
    end;
    get_nb_objets:=i;
end;

//
// renvoie un pointeur vers l'objet recherch�// @param pdeb : pointeur de d�ut de liste
// @param k : num�o de l'objet recherch�(index��0)
// @return pointeur vers l'objet recherch�function get_objet(pdeb : pliste_objets ; k : integer):pcomp_objet;
var pl : pliste_objets;
    i : integer;
begin
    pl := pdeb;
    i:=0;
    
    while (not is_empty(pl)) and (i <> k) do
    begin
        pl := pl^.psuivant;
        i := i + 1;
    end;
    get_objet := @(pl^.objet);
end;

//
// renvoie le num�o de l'objet vers lequel pointe pobj
// @param pdeb : pointeur de d�ut de liste
// @param pobj : pointeur vers l'objet
// @return num�o de l'objet
function get_id_objet(pdeb : pliste_objets ; pobj : pcomp_objet):integer;
var pl : pliste_objets;
    i : integer;
begin
    pl := pdeb;
    i:=0;
    while (not is_empty(pl)) and (@(pl^.objet) <> pobj) do
    begin
        pl := pl^.psuivant;
        i := i + 1;
    end;
    get_id_objet := i;
end;

// 
// affiche les infos (x,y,x0,y0x1,y1,Surface) d'un objet
// @param pdeb : pointeur de d�ut de liste
// @param objet : pointeur vers l'objet
procedure print_infos_objet(pdeb : pliste_objets; objet : pcomp_objet);
begin
    if(DEBUG) then 
    begin
        writeln;
        writeln('Objet ', get_id_objet(pdeb, objet), ' : ');
        writeln('x=',objet^.x);
        writeln('y=',objet^.y);
        writeln('x0=',objet^.x0);
        writeln('y0=',objet^.y0);
        writeln('x1=',objet^.x1);
        writeln('y1=',objet^.y1);
        writeln('s=',objet^.surface);
        writeln;
    end;
end;

//
// efface l'objet recherch�de la liste
// @param pdeb : pointeur de d�ut de liste
// @param k : num�o de l'objet �effacer
procedure delete_objet(var pdebx : pliste_objets ; k : integer);
var pl,pltmp : pliste_objets;
    i : integer;
begin
    
    if(DEBUG) then writeln('Suppression de l''objet ',k);
     
    // objet introuvable
    if((k>=get_nb_objets(pdebx)) or (k<0)) then 
    begin
        writeln('Objet ',k,' introuvable dans la liste');
    end
    else
    // on procede la suppression
    begin
        print_infos_objet(pdebx, get_objet(pdebx,k));
        
        // cas suppression objet 0
        if(k=0) then
        begin
            pdebx := (pdebx^.psuivant);
        end
        // cas suppression objet 1
        else if(k=1) then
        begin
            pdebx^.psuivant := (pdebx^.psuivant)^.psuivant;
        end
        // suppression objet k > 1
        else
        begin
            pl := pdebx;
            pltmp := pdebx;
            i:=0;
            while ((i <> k) and (not is_empty(pl))) do
            begin
                i := i + 1;
                pltmp := pl;
                pl := pl^.psuivant;
            end;
               
            // cas suppression dernier objet
            if(k=get_nb_objets(pdebx)-1) then
            begin
                pltmp^.psuivant := nil;
            end
            else
            begin
                pltmp^.psuivant := pl^.psuivant;                         
            end;
            
            dispose(pl);
        end;
    end;
end;

//
// ajoute un objet en queue de liste
// @param pdebbis : pointeur de d�ut de liste
// @param x,y : coordonn�s de l'objet �ajouter
// @note le pointeur pdeb est modifi�au premier appel de la fonction
procedure add_objet(var pdeb : pliste_objets; x,y : integer);
var p1,p2 : pliste_objets;
begin
    p2 := pdeb;

    New(p1);
    p1^.psuivant := Nil;

    
    if p2<>nil then
    begin
        while(p2^.psuivant<>Nil) do
        begin
            p2 := p2^.psuivant;
        end;
    p2^.psuivant := p1;
    end
    else pdeb:=p1;


    p1^.objet.x := x;
    p1^.objet.y := y;
    p1^.objet.x0 := x;
    p1^.objet.y0 := y;
    p1^.objet.x1 := x;
    p1^.objet.y1 := y;
    p1^.psuivant := nil;

    if(DEBUG) then writeln('-> Objet ',get_id_objet(pdeb, @(p1^.objet)),' ajoute');
end;

//
// remplit tous les pixels par le num�o de l'objet k
// @param x,y : coordonn�s de l'objet �ajouter
// @param k : num�o objet (valeur de la composante des pixels)
// @param img : pointeur vers l'image sur laquelle on travaille
// @param pdeb : pointeur de d�ut de liste
procedure remplir_objet(x,y,k : integer; img: pTimage; pdeb : pliste_objets);
var obj : pcomp_objet;
begin
    if(read_pixel(x,y,img).red > k) then
    begin
        write_pixel(x,y,img,k,k,k);

        obj := get_objet(pdeb, k);

        // maj des valeurs min/max
        if(obj^.x0 > x) then obj^.x0 := x;
        if(obj^.y0 > y) then obj^.y0 := y;
        if(obj^.x1 < x) then obj^.x1 := x;
        if(obj^.y1 < y) then obj^.y1 := y;

        // surface
        obj^.surface := obj^.surface + 1;
        
        
        // appels recursifs
        remplir_objet(x,y-1,k,img,pdeb);
        remplir_objet(x,y+1,k,img,pdeb);
        remplir_objet(x-1,y,k,img,pdeb);
        remplir_objet(x+1,y,k,img,pdeb);
    end;
end;

//
// parcourt l'image et cherche les objets diff�ents puis les remplit par leurs num�os
// @param img : pointeur vers l'image sur laquelle on travaille
// @param pdeb : pointeur de d�ut de liste
procedure capture_objet(img : pTimage; var pdeb : pliste_objets);
var k,i,j : integer;
begin

    k:=0;

    for i:=0 to img^.largeur do
    begin
        for j:=0 to img^.hauteur do
        begin
            if(read_pixel(i,j,img).red = 255) then
            begin
                add_objet(pdeb,i,j);
                
                remplir_objet(i,j,k,img,pdeb);
                
                print_infos_objet(pdeb, get_objet(pdeb, k));

                k := k + 1;
            end;
        end;
    end;
end;

//
// supprime de la liste et efface de l'image les objets qui ne respectent pas les contraintes min/max sur mesure
// @param img : pointeur vers l'image sur laquelle on travaille
// @param pdeb : pointeur de d�ut de liste
// @param mesure : type de mesure �contraintes (voir d�inition du type)
// @param min, max : valeurs maximales de la mesure
// @note : min/max ont le flag -1 pour signifier "Pas de contrainte min/max"
procedure supprime_objets_hors_gabarit(img : pTimage; var pdebx : pliste_objets; mesure : mesures; min, max : real);
var pl : pliste_objets;
    i,initi : integer;
    objsave : comp_objet;
begin

    pl := pdebx;
    
    // compteur de l'objet ancienne base
    initi := 0;
    // compteur objet nouvelle base (apres suppression(s))
    i := initi;

    // on parcourt tous les objets
    while (not is_empty(pl)) do
    begin
        // pour chacun on regarde si la mesure est fausse
        // -1 est le flag = pas de contrainte sur cette valeur
       if(
       ((min <> -1) and (lire_mesure_objet(@(pl^.objet), mesure) < min)) or 
       ((max <> -1) and (lire_mesure_objet(@(pl^.objet), mesure) > max))
       ) then
       begin
           if(DEBUG) then writeln('L''objet initialement ',initi,' a une mauvaise mesure',lire_mesure_objet(@(pl^.objet), surface));
                              
           // on l'efface de l'image
           remplir_objet(pl^.objet.x,pl^.objet.y,255,img, pdebx);
             
           // et on supprime l'objet de la liste
           delete_objet(pdebx, i);
             
           // !!! on derecremente i car on a supprime un objet !!!
           dec(i);
           
       end; 
       inc(i);
       inc(initi);
       pl := pl^.psuivant;
    end;
    
end;

//
// ajoute des rectangles autour des objets de la liste sur l'image 
// @param img : pointeur vers l'image sur laquelle on travaille
// @param pdeb : pointeur de d�ut de liste
// @return image modifi�
function add_rect_objets(img : pTimage; pdeb : pliste_objets):pTimage;
var img2 : pTimage;
    pl : pliste_objets;
    i, coulr, coulg, coulb : integer;
begin
    img2:=new_image();
    img2^ := img^;
    pl:=pdeb;
    
    // couleur du rectangle
    coulr := 255;
    coulg := 0;
    coulb := 0;
    
    // pour chaque objet de la liste
    while (not is_empty(pl)) do
    begin

        // trait horizontal du carre
	for i:=pl^.objet.x0 to pl^.objet.x1 do
        begin
            write_pixel(i,pl^.objet.y0, img2, coulr, coulg, coulb);
            write_pixel(i,pl^.objet.y1, img2, coulr, coulg, coulb);
        end;
        // trait vertical du carre
	for i:=pl^.objet.y0 to pl^.objet.y1 do
        begin
            write_pixel(pl^.objet.x0, i, img2, coulr, coulg, coulb);
            write_pixel(pl^.objet.x1, i, img2, coulr, coulg, coulb);
        end;
        pl := pl^.psuivant;
    end;
    add_rect_objets:=img2;
end;

//
// affiche infos de tous les objets
// @param pdeb : pointeur de d�ut de liste
procedure print_all_infos_objets(pdeb : pliste_objets);
var i : integer;
begin
    for i:=0 to get_nb_objets(pdeb)-1 do
    begin
        print_infos_objet(pdeb, get_objet(pdeb, i));
    end;
end;

//
// retourne une propri��d'un objet
// @param objet : pointeur vers l'objet
// @param mesure : type de propri���retourner
// @return real valeur de la propri��function lire_mesure_objet(objet : pcomp_objet; mesure : mesures):real;
begin
    case mesure of
        largeur: lire_mesure_objet:=abs(objet^.x1-objet^.x0);
        hauteur: lire_mesure_objet:=abs(objet^.y1-objet^.y0);
        surface: lire_mesure_objet:=objet^.surface;
        // perimetre: lire_mesure_objet:=objet^.perimetre;
        rapport_surface_perimetre:
lire_mesure_objet:=objet^.surface/objet^.perimetre;
        rapport_hauteur_largeur: lire_mesure_objet:=abs(lire_mesure_objet(objet,hauteur)/lire_mesure_objet(objet,largeur));
        rapport_taille_surface: lire_mesure_objet:=abs((objet^.x1 - objet^.x0)*(objet^.y1 - objet^.y0)/lire_mesure_objet(objet,surface));
    end;
end;

//
// main
begin
end.
