Module Interface (.ml)

module Interface: sig .. end
Projet Puzzle
Author(s): Lilian Besson <lilian.besson[AT]ens-cachan.fr>

val ex : unit -> unit
val d_d : string -> int
val print_signature : unit -> int

Cette interface graphique se base sur le contenu de 'resolution.ml'. Il faut noter qu'un changement dans la librairie de resolution devrait ne pas rendre cette interface obsolète, du moment que les types et les signatures des fonctions principales soient conservées.

Utilise aussi le fichier 'temps.ml'. Pour mesurer le temps mis par une résolution !.

On doit batir une interface graphique simple sur ces fonctions là.

Fonctions outils
val init_fenetre : x:int -> y:int -> unit -> unit
Pour ouvrir et mettre un titre à notre fenêtre.
val from_rgb : Graphics.color -> int * int * int
Pour récupérer les coordonnées (r,g,b) d'une couleur du module Graphics.
val inv_color : Graphics.color -> Graphics.color
Pour inverser les couleurs d'une image.
val array_ot_triple : 'a * 'a * 'a -> 'a array
Passe d'un triplet à un tableau.
val draw_rect : int -> int -> int -> int -> unit
Pour dessiner une rectangle.
val draw_int : x:int -> y:int -> i:int -> unit
Pour écrire un entier en commancant en un pixel précis.

Fonctions de base
val couleur_fond : Graphics.color
On définit quelques constantes, afin que toutes les fonctions suivantes utilisent la même couleur de fond, et la même couleur de tracés.
val couleur_lignes : Graphics.color

Pour générer une échelle de couleurs.
val genere_echelle_couleur : cmin:Graphics.color -> cmax:Graphics.color -> nb:int -> Graphics.color array
On procède uniquement par des dégradés : il faut choisir deux couleurs (min et max), et le nombre de couleurs voulues, et ca nous renvoit une échelle.
val echelle_gris : int -> Graphics.color array
Une échelle de gris, simple.
val echelle_rouge : int -> Graphics.color array
Une échelle de rouge, simple.
val echelle_vert : int -> Graphics.color array
Une échelle de vert, simple.
val echelle_jaune : int -> Graphics.color array
Une échelle de jaune, simple.
val echelle_bleu : int -> Graphics.color array
Une échelle de bleu, simple.
val echelle_violet : int -> Graphics.color array
Une échelle de violet.
val echelle_metal : int -> Graphics.color array
Une échelle du jaune au rouge (style métal en fusion).

Un outil utilisant 'zenity' pour demander à l'utilisateur de rentrer deux couleurs pour l'échelle.
exception Error_Annule of int
Pour gérer le code de retour d'un processus Unix lancé par la commande "Sys.command" en ocaml. Ce n'est pas très très propre de faire comme ca, mais ca marche bien.
val faire : string -> unit
Un peu de scripting en Ocaml. La fonction suivante execute la commande Unix contenue dans la chaine argument, en renvoyant une exception si la commande ne s'execute pas comme il faut (code d'erreur de retour unix non nul).
val zenity_info : string -> unit
Afficher une fenètre informative, qui demande juste à l'utilisateur de cliquer pour continuer. Appuyez sur Echap ou fermer cette fenètre lève une exception.
val zenity_notif : string -> unit
Idem.
val string_of_char : char -> string
Transforme un char en string.
val int_of_charhex : char -> int
Transforme un char en héxa en sa valeur entière. Accepte aussi bien les minuscules que les majuscules.
val int_of_hex2 : string -> int
Transforme un caractère bi hexa (chaine de deux caractères entre 0 et A) en sa valeur.
val parse_color : string -> Graphics.color
Petite fonction outil pour récupérer le code RGB utilisé par Graphics, à partir du rendu utilisé par Zenity dans sa fonction "--color-selection".
val parse_int : string -> int
Petite fonction outil pour extraire un entier du fichier texte se trouvant à l'adresse "file". Cela suppose bien sur que le dit fichier ne contiennent qu'un entier.
val ask_couleur : unit -> Graphics.color
Utilise zenity et les fonctions précédentes pour demander à l'utilisateur de choisir une couleur, en ouvrant une roue des couleurs RGB, l'enregistre dans un fichier temporaire, l'extraie via la fonction "parse_color", et la renvoie.
val ask_int : unit -> int
Utilise zenity et les fonctions précédentes pour demander à l'utilisateur de choisir un entier, en ouvrant un curseur, l'enregistre dans un fichier temporaire, et la renvoie. Comme cette fonction sera utilisée pour demander le nombre de colonnes, lignes et couleurs, on limite les valeurs qu'il est possible de choisir à etre entières, entre 1 et 16.
val ask_int_msg : string -> unit -> int
La meme mais avec un message customisable.
val echelle_intelligente : int -> Graphics.color array
Une échelle adaptée selon le nombre de couleurs. Si c est petit, on prend du gris dégradé, sinon on prend un patchwork.
val ask_echelle_couleur : int -> unit -> Graphics.color array
Utilise 'ask_couleur' pour demander à l'utilisateur de choisir une échelle de couleur.

Par défaut, si l'utilisateur refuse de choisir deux couleurs, on renvoit un échelle de gris adaptée au nombre de couleurs différentes voulues.
val bijection_couleur_color : Graphics.color array -> Resolution.couleur -> Graphics.color
Pour transformer ce 'Graphics.color array' en une fonction : couleur -> Graphics.color. Il faut supposer que l'échelle comporte le bon nombre de couleurs. Sinon le résultat produit n'est pas correct (ou en tout cas, on ne peux pas assurer qu'il le soit).
val fill_custom : Resolution.couleur -> (int * int) array -> unit -> unit
Pour remplir un polygone d'une certaine couleur, tout en sauvegardant la couleur courante et le point courant.
val line_custom : Resolution.couleur -> int -> int -> int -> int -> unit -> unit
Pour dessiner une ligne d'une couleur donnée, tout en sauvegardant la couleur courante et le point courant.
val rose : int
Echelle exemple : les exemples seront fait avec 4 couleurs, alors autant avoir une échelle de couleur qui pète un peu pour les exemples !
val mandarine : int
val orange : int
val jaunefaible : int
val saumon : int
val violet : int
val indigo : int
val turquoise : int
val vertjaune : int
val fbij_jolie_quatorze : c:int -> Resolution.couleur -> Graphics.color
Echelle exemple jolie pour moins de 14 couleurs differentes.
val fbij_jolie_plus : c:int -> Resolution.couleur -> Graphics.color
C'est celle là qu'on change selon le choix de l'utilisateur.
val echelle_globale_fichier_initialisee_une_fois : Graphics.color array Pervasives.ref
L'échelle de couleur utilisée par défaut dans le fichier.
val change_echelle_globale : int -> unit -> unit
Pour changer la valeur de l'échelle de couleur globale utilisée dans l'interface. Utilise "Zenity" et 'ask_echelle_couleur' décrit plus haut.
val choisi_echelle_pertinente : 'a -> Resolution.couleur -> Graphics.color
Pour se débrouiller seul : une fonction qui choisit l'échelle pertinente en fonction du nombre de couleurs. En fait, elle convertie seulement l'échelle globale en une fonction qui prend un entier (ie : une des informations associées à un coin de la pièce) et la convertie en Graphics.color.

On va créer une fonction qui prendre une échelle de taille, une pièce, et affichera la pièce dans la fenêtre Graphics ouverte. Attention, suppose qu'on à une fenêtre ouverte (sinon, 'fatal error' !).

L'argument 's' correspond à la taille en pixel de la moitié de la pièce, et est optionnel.

L'argument 'c' correspond au nombre de couleurs.
val afficher_piece : ?x:int -> ?y:int -> p:Resolution.piece -> ?s:int -> c:int -> unit
Les arguments 'x' et 'y' correspondent aux coordonnées du coin inférieur gauche de la pièce dessinée.
val afficher_piece_option : ?x:int -> ?y:int -> p:Resolution.piece option -> ?s:int -> c:int -> unit
Celle-là est un simple raffinement de la précédente, elle ne fait rien si on lui donne un 'None' (je rappelle qu'un None dans une case du plateau signifie que rien n'a encore été fixé sur cette case là).
val c : int
val p0 : Resolution.piece
val new_piece : int -> unit
Une petite fonction test, qui dessine une pièce aléatoire au point courant.

Squelette du programme d'interface.
exception Fin
Si une action fait arréter l'interface.
val squel : f_init:(unit -> 'a) ->
f_end:(unit -> unit) ->
f_key:(char -> unit) ->
f_mouse:(int -> int -> unit) -> f_except:(exn -> unit) -> unit
Générateur d'interface. Librement inspiré du livre 'Développement d'applications en Ocaml'.

Un essai d'utilisation de ce squelette d'interface.
val trait_char : int -> char -> unit
On fait une première interface simpliste, qui dessine une pièce au point courant dès qu'on tape sur 'Espace'.
val exemples_piece_aleatoire : int -> unit
Ce premier exemple était satisfaisant.
exception Erreur_trop_grand_h
Deux exceptions si on veut tracer un plateau trop grand pour la taille de fenètre qu'on peut utiliser. Je ne sais pas encore comment faire une fenètre qui défile en OCaml avec le module Graphics.
exception Erreur_trop_grand_v
val resolution_x : int
Paramètres GLOBAUX des tracés. La résolution d'écran correspondante doit être supérieure a 1000x700 : 1024*768 ou mieux. Malgré cela, normalement tout marche sur des résolutions plus petite, seulement la détection d'une demande de tracés impossible ne sera pas faite. Si vous avez un écran plus petit, ne demandez pas trop de pièces ;) !
val resolution_y : int
val taille_piece : int
val afficher_plateau : Resolution.plateau -> unit
Pour afficher un plateau.
val test_random : m:int -> n:int -> c:int -> nb:int -> unit
Un petit test pour vérifier cette fonction.
val actualise_taille : s:int -> n:int -> m:int -> unit -> unit
val lit_et_affiche_plateau : file:string -> unit
Pour lire un plateau dans un fichier, et l'afficher, directement.
val afficher_instance : Resolution.instance -> unit
Pour afficher une instance avant qu'elle ne soit résolue.
val lit_et_affiche_instance : file:string -> unit
Pour lire une instance dans un fichier, et l'afficher, directement.
val creer_instance_random_et_affiche : m:int -> n:int -> c:int -> Resolution.instance
Pour générer et afficher une instance aléatoire.
val aff_random : m:int -> n:int -> c:int -> Resolution.instance
Un alias, plus court.

Pour afficher une instance, demander confirmation avant de lancer la résolution, puis résoudre en mode interactif (affiche le plateau courant a chaque étape, et affiche des infos log sur le déroulement des opérations), on va utiliser ce qui suit.
val printP : Resolution.plateau -> unit
Pour etre utilisee avec les fonctions de la librairie 'resolution.ml'. Permet d'afficher en couleur un plateau, puis de demander confirmation à l'utilisateur avant de continuer. Attention : a besoin d'une fenètre DEJA ouverte !
val printP_chrono : Resolution.plateau -> unit
La meme en affichant le temps du chronomètre.
val printP_chrono_no : 'a -> unit
La meme en affichant le temps du chronomètre, mais sans afficher le plateau.
val resoud_naivement_graphique_nochrono : Resolution.instance -> Resolution.plateau
Utilise la résolution naïve décrite dans 'resolution.ml' mais avec l'affichage à l'écran, en couleur, intermédiaire des plateaux testés.
val resoud_naivement_graphique : Resolution.instance -> Resolution.plateau
Avec chronometre. Attention : suppose que le chronometre du module 'Temps' a déja était lancé via l'appel à 'Temps.lancer_chrono ()'.
val resoud_naivement_no_graphique : Resolution.instance -> Resolution.plateau
Avec chronometre. Attention : suppose que le chronometre du module 'Temps' a déja était lancé via l'appel à 'Temps.lancer_chrono ()'.
val aide_interface : string
Le contenu de l'aide de l'interface graphique.
val aide_ic : unit -> unit
Pour afficher cette aide dans une fenètre.
val new_n_ic : unit -> int
Trois alias, pour demander facilement de nouvelles valeurs pour un des trois paramètres de l'interface : le nombre de lignes, de colonnes ou de couleurs. Ces trois fonctions de demandent de valeurs utilisent "Zenity".
val new_m_ic : unit -> int
val new_c_ic : unit -> int

Les deux fonctions suivantes sont pour demander à l'utilisateur un fichier dans un répertoire, pour charger une instance enregistrée auparavant.
val parse_fichier : string -> string
Renvoie le contenu de la première ligne d'un fichier texte.
val ask_fichier : unit -> string
Et j'utilise cela pour avoir une fonction intéractive de demande de fichier.
val ask_value : string -> unit -> string
Pour réaliser une capture d'écran, le système invite l'utilisateur à choisir un nom pour sa capture, ce qui est réalisé avec cette fonction là.
val browse_instance : unit -> Resolution.instance
Condense deux actions, pour permettre de directement renvoyé l'instance choisie dans le répertoire.
val demande_confirmation_graphique : string -> unit
Pour demander confirmation avant d'écraser un fichier. De façon graphique, et dans le terminal affiche aussi un message (mais n'attend pas de réponse !).
val ecrit_instance_browse_2 : Resolution.instance -> unit -> unit
val ecrit_instance_browse : Resolution.instance -> unit -> unit
Pour sauvegarder l'instance (ou le plateau) affiché à l'écran, on utilise cette fonction là.
val capture_ecran : unit -> unit
Pour réaliser une capture d'écran. On demande d'abord un nom de fichier, puis on utilise l'outil "Import", gratuitement disponible sous Linux, pour capturer le contenu de la fenêtre ayant pour titre "Projet Puzzle".

Attention, si deux fenêtres sont ouvertes avec le même nom, la 'sémantique' de la réaction de l'outil Import n'est pas définie. Et il y aura très certainement une erreur.
val titre_ic : c:int -> n:int -> m:int -> unit
Macro pour actualiser rapidement le titre de la fenêtre en fonction des trois paramètres locaux n, m, et c.
val fail_recherche : Resolution.instance -> unit -> unit
Si la recherche d'une solution à l'instance argument a échouée, affiche un message d'erreur, et actualise le titre de la fenêtre en fonction de cela.

Pour passez dans le mode ou l'utilisateur peut choisir une pièce, en rentrant ses informations, et la fixer sur une des cases du plateau, avant de lancer la complétion aléatoire par exemple.
val ask_piece : c:int -> unit -> Resolution.piece
Pour demander une pièce a l'utilisateur. Demande les paramètres locaux n, m, c.
val ask_position : n:int -> m:int -> s:int -> unit -> int * int
Pour choisir la position de la pièce, à la souris. Demande aussi 's', le paramètre d'échelle utilisé pour la taille des pièces, afin de pouvoir retrouver les indices (i,j) correspondant à la zone cliquée.
val interface_complete : unit -> unit
Et enfin l'interface complète, qui utilise le squelette d'interface présenté plus haut, et toutes les fonctions précédentes.

Aide de l'interface graphique :

Ce programme est écrit en Ocaml, et utilise le module graphique de la distribution standard Graphics pour l'affichage, et l'outil Zenity, disponible gratuitement sous Linux (''$ sudo apt-get install zenity'' pour le récupérer).

Durant la boucle intéractive, les touches suivantes déclenchent les actions suivantes : h : affiche ce message d'aide, 'enter' : efface la fenêtre, 'e' fait la meme chose (le code de la touche 'enter' varie selon le mode d'exécution). u : change le nombre de couleurs utilisées, c : change l'échelle de couleur utilisée, m : change le nombre de colonnes des instances générées, n : change le nombre de lignes des instances générées, 'Escape': quitte proprement et enregistre l'instance / le plateau courant dans un fichier de sauvegarde caché, i : sauvegarde l'image de la fenêtre dans une image JPEG externe, en demandant un nom de fichier (demande confirmation avant écrasement). Utilise l'outil 'import', disponible gratuitement sous Linux (''$ sudo apt-get install imagemagick'' pour le récupérer), s : sauvegarde l'instance / le plateau courant affiché à l'ecran dans un fichier texte externe, en demandant un nom de fichier (demande confirmation avant écrasement). b : ouvre une instance déjà sauvegardée, en utilisant le navigateur de document de Zenity. 'space' : génére une nouvelle instance aléatoire avec les paramètres courant, et l'affiche, w : génére une nouvelle instance aléatoire, ayant une solution, avec les paramètres courant, et l'affiche, y : génére une solution aleatoire, avec les paramètres courant, et l'affiche, p : demande de choisir une pièce, puis une position pour cette pièce, et enfin confirmation pour la placer dans l'instance courante. Redessine celle ci ensuite. Un clic de la souris sur une position de pièce fait la même chose. v : pour vérifier la validité du plateau dessiné à l'écran, et afficher le résultat (valide, ou une preuve de non validité) dans une fenêtre informative. r : résoud l'instance courante affichée à l'écran,

Appuyez sur Echap pour fermer cette fenêtre d'aide et retourner au programme. Evitez de quitter brutalement, la gestion souple d'une 'fatal error' n'est pas encore au point (ceci est valable pour tout le programme).