/*
Fichier cylgraph.c
Auteur Bernard Chardonneau
Logiciel libre, droits d'utilisation précisés en français
dans le fichier : licence-fr.txt
Traductions des droits d'utilisation dans les fichiers :
licence-en.txt , licence-es.txt , licence-pt.txt ,
licence-eo.txt , licence-eo-utf.txt
Droits d'utilisation également sur la page web :
http://cyloop.tuxfamily.org/voir.php?page=droits
Programme qui génère un graphique à partir des données
du fichier cyloop.
*/
#define appli
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "types.h"
#include "limites.h"
#include "messages.h"
#include "image.h"
#include "gentexte.h"
#include "bmp.h"
// longueur maximum d'un nom de fichier
#define szmax_nomfic 80
// nombre maximum de courbes dans le graphique
#define max_trace 12
// taille des flèches tracées
#define taillefleche 5
#define deborde_trait 5
// prototypes
char *recup_nomfic (char *nom);
void trtargs (int narg, char *varg[]);
void charge_val ();
void charge_val_uint16 (int numtrace);
void charge_val_uint32 (int numtrace);
void charge_val_int32 (int numtrace);
void charge_val_float (int numtrace);
void charge_minmax_int32 (int numtrace);
void charge_minmax_float (int numtrace);
void initpalette ();
void memgris (octet luminosite, octet numcoul);
void memcoul (int32 couleur);
void majtaille_image ();
void init_image ();
void init_graphique ();
void posit_graphique ();
void gen_echelle_x ();
int trait_vert_quadri (float position);
void rech_minmax ();
void gen_echelle_y ();
int trait_horiz_quadri (float position);
void trace_graphique ();
void trace_seg (int x, int ydeb, int yfin, octet couleur, int epaisseur);
void ajout_textes ();
void insert_graphique ();
void ajout_fleche_droite (int x, int y);
void ajout_fleche_haut (int x, int y);
void genficimage (char *nomimage);
// variables globales (pour éviter des tas de passages de paramètre)
// d'autres variables sont définies dans image.h
FILE *descfic; // descripteur du fichier cyloop
desc_cyloop entetefic; // entête du fichier cyloop
desc_var entetevar; // entête de la variable du fichier cyloop
int larg_graph = 0; // largeur du graphique
int haut_graph = 0; // hauteur du graphique
int deb_x, deb_y; // coordonnées du début du graphique dans l'image
octet *graphique; // graphique intégré à l'image
char *titre = NULL; // titre de l'image
int optp = 0; // option échelle verticale en pourcentage
int nbtrace = 0; // nombre de tracés
octet typetrace [max_trace]; // type des tracés (min, max ou valeur)
octet epaisseur [max_trace]; // épaisseur des trait ou surface
float *listeval [max_trace]; // liste des valeurs pour chaque tracé
float mingraph, maxgraph; // min et max sur l'axe vertical du graphique
// programme principal
main (int narg, char *varg[])
{
char *nomfic; // nom du fichier cyloop
char *nomimage; // nom du fichier image
// mémorisation du nom de la commande
memcom (*varg);
// récupération du nom de fichier cyloop et rajout éventuel du suffixe
if (--narg > 3)
nomfic = recup_nomfic (*(++varg));
else
{
// "Trop peu d'arguments"
affiche_err ("ARGS_MANQUANTS");
putchar ('\n');
// "Syntaxe : %s nom_fichier_cyloop nom_fichier_image"
// " options_générales descriptions_tracés"
//
// "Options générales :"
// "[-h hauteur_graphique] [-w largeur_graphique] [-H hauteur_image]"
// "[-W largeur_image] [-T titre_image] -p"
//
// "Description des tracés : -[m|M](tépaisseur|a) couleur"
aff_err_arg ("SYNT_CYLGRAPH1", nomcom ());
affiche_err ("SYNT_CYLGRAPH2");
putchar ('\n');
affiche_err ("SYNT_CYLGRAPH3");
affiche_err ("SYNT_CYLGRAPH4");
affiche_err ("SYNT_CYLGRAPH5");
putchar ('\n');
err_fatale ("SYNT_CYLGRAPH6");
}
// tentative de lecture du fichier cyloop
descfic = fopen (nomfic, "r");
if (! descfic)
// "Fichier_cyloop %s inexistant ou protégé en lecture"
err_fatale_arg ("CYLOOP_ABSENT", nomfic);
// lecture de l'entête du fichier cyloop
fread (&entetefic, 16, 1, descfic);
// vérification de la signature
if (entetefic.signature [0] != 'c' || entetefic.signature [1] != 'y'
|| entetefic.signature [2] != 'l')
{
// "Le fichier %s n'est pas un fichier_cyloop"
aff_err_arg ("FICH_NON_CYLOOP", nomfic);
fclose (descfic);
return;
}
// vérification de la valeur nb_var
if (entetefic.nb_var)
{
// "Ce fichier cyloop utilise une liste de variables"
// "utiliser la version complète de cyloop"
affiche_err ("CYLOOP_LISTEVAR");
affiche_err ("UTIL_VCOMPLETE");
fclose (descfic);
return;
}
// récupération du nom de fichier image
nomimage = *(++varg);
// initialisation des couleurs de base de l'image
initpalette ();
// traiter les arguments resta,ts de la ligne de commande
trtargs (narg, varg);
// mémoriser les valeurs de la variable du fichier cyloop
charge_val ();
// plus besoin d'accéder au fichier cyloop
fclose (descfic);
// calculer ou corriger la taille du graphique et de l'image globale
majtaille_image ();
// générer l'image de base (fond)
init_image ();
// générer le graphique de base (fond et quadrillage)
init_graphique ();
// positionner le graphique dans l'image
posit_graphique ();
// tracé de l'échelle sur l'axe x
gen_echelle_x ();
// rechercher les valeurs min et max du graphique
rech_minmax ();
// tracé de l'échelle sur l'axe y
gen_echelle_y ();
// rajouter les tracés dans le graphique
trace_graphique ();
// rajouter les commentaires dans l'image
if (titre)
aff_texte (titre, haut_totale - 10, larg_totale / 2, align_centre);
ajout_textes ();
// insérer le graphique dans l'image
insert_graphique ();
// générer le fichier image
genficimage (nomimage);
}
// récupération du nom du fichier cyloop et rajout éventuel de son suffixe
char *recup_nomfic (char *nom)
{
char *nomfic;
int lg_nomfic;
// récupérer la taille du nom de fichier
lg_nomfic = strlen (nom);
// si ce nom se termine par le suffixe .cyl
if ((lg_nomfic > 4) && (strcmp (nom + lg_nomfic - 4, ".cyl") == 0))
{
// mémoriser le nom tel quel
nomfic = malloc (lg_nomfic + 1);
strcpy (nomfic, nom);
}
// sinon
else
{
// rajouter au nom le suffixe .cyl et mémoriser le résultat
nomfic = malloc (lg_nomfic + 5);
strcpy (nomfic, nom);
strcpy (nomfic + lg_nomfic, ".cyl");
}
// retourner le nom du fichier
return nomfic;
}
// prise en compte des arguments restants de la ligne de commande
void trtargs (int narg, char *varg[])
{
int optgen; // prise en compte des options générales des l'image
char optmM; // caractère d'une option min ou max de cylgraph
char option; // caractère d'une option de cylgraph
char *posopt; // adresse du caractère d'une option de cylgraph
int32 couleur; // couleur d'un élément du graphique
// se positionner sur les options de la commande cylgraph
narg = narg - 2;
varg ++;
// prise en contre des options générales à l'image
optgen = OUI;
while (**varg == '-' && optgen)
{
option = varg [0][1];
switch (option)
{
// hauteur du graphique
case 'h' : haut_graph = atoi (varg [1]);
break;
// hauteur de l'image
case 'H' : haut_totale = atoi (varg [1]);
break;
// largeur du graphique
case 'w' : larg_graph = atoi (varg [1]);
break;
// largeur de l'image
case 'W' : larg_totale = atoi (varg [1]);
break;
// titre de l'image
case 'T' : titre = varg [1];
break;
// option pourcentage pour l'échelle verticale
case 'p' : optp = 1;
// pour compenser le saut de 2 arguments successifs
narg ++;
varg --;
break;
// fin des options générales
default : optgen = NON;
}
// passer aux arguments suivants
if (optgen)
{
varg = varg + 2;
narg = narg - 2;
}
}
// lecture de la partie fixe de l'entête de la variable du fichier cyloop
fread (&entetevar, 6, 1, descfic);
// mémorisation des caractéristiques des graphiques
while (narg > 1 && **varg == '-')
{
optmM = varg [0][1];
// demande de tracé de la valeur min ou max ?
if (optmM == 'm' || optmM == 'M')
{
// s'il est mémorisé pour la variable
if (entetevar.typedon & mem_min_max)
{
// mémoriser la demande
typetrace [nbtrace] = optmM;
posopt = &varg [0][2];
}
// sinon
else
{
// la demande ne sera pa conservée
typetrace [nbtrace] = 0;
// générer le message d'erreur adéquat
if (entetevar.typedon & compteur)
// "Variable compteur, les minimums et maximums ne sont pas mémorisés"
affiche_err ("MINMAX_COMPTEUR");
else
// "Pas de mémorisation des minimums et maximums pour cette variable"
affiche_err ("MINMAX_MANQUANT");
}
}
// sinon
else
{
// mémoriser un tracé des valeurs de la variable
typetrace [nbtrace] = 'v';
posopt = &varg [0][1];
}
// si demande de tracé valide
if (typetrace [nbtrace])
{
// choix entre trait et surface
option = *posopt;
switch (option)
{
// tracé d'une surface
case 'a' : epaisseur [nbtrace++] = 255;
break;
// tracé d'un trait de 1 à 3 pixels d'épaisseur
case 't' : if ('1' <= *(posopt + 1) && *(posopt + 1) <= '9')
epaisseur [nbtrace++] = *(posopt + 1) - '0';
else
epaisseur [nbtrace++] = 1;
break;
// "Option -%c inconnue ou mal placée"
default : aff_err_argnum ("OPTION_INCONNUE", option);
}
// récupération de la couleur de ce tracé
if (option == 'a' || option == 't')
{
sscanf (varg [1], "%lX", &couleur);
memcoul (couleur);
}
}
// passer aux arguments suivants
varg = varg + 2;
narg = narg - 2;
}
if (narg > 0)
{
// "Erreur dans les paramètres à partir de %s"
aff_err_arg ("ERR_PARAMETRES", *varg);
}
}
// mémoriser les valeurs de la variable du fichier cyloop
void charge_val ()
{
int taillelisteval; // nombre d'octet d'un tableau listeval
int numtrace; // numéro de tracé
// calcul de la taille d'une zone mémoire mémorisant les valeurs d'un tracé
taillelisteval = entetefic.nb_don_cycle * sizeof (float);
// pour chaque tracé à réaliser dans le graphique
for (numtrace = 0; numtrace < nbtrace; numtrace++)
{
// allouer une zone mémoire pour extraire les données du fichier cyloop
listeval [numtrace] = malloc (taillelisteval);
// terminé si échec
if (! listeval [numtrace])
// "Manque de place mémoire pour générer un graphique"
err_fatale ("MANQUE_MEMOIRE1");
// et l'initialiser avec une valeur flottante indéterminée
memset (listeval [numtrace], 0xFF, taillelisteval);
// positionnement sur l'entête de la variable
fseek (descfic, 16, SEEK_SET);
// lecture de la partie fixe de l'entête de la variable
fread (&entetevar, 6, 1, descfic);
// et de coef_moy s'il est global à la variable
if (! (entetevar.typedon & cmoy_sep))
fread (&entetevar.coef_moy, 2, 1, descfic);
// extraire les valeurs de la variable du fichier cyloop
// si compteur
if (entetevar.typedon & compteur)
{
// 2 formats de données sont possibles
if (entetevar.typedon & donnees_32bits)
charge_val_uint32 (numtrace);
else
charge_val_uint16 (numtrace);
}
// sinon, mémorisation de valeurs
else
{
// si tracé de la valeur moyenne de la variable durant le cycle
if (typetrace [numtrace] == 'v')
{
// 2 formats de données sont possibles dans le fichier cyloop
if (entetevar.typedon & val_float)
charge_val_float (numtrace);
else
charge_val_int32 (numtrace);
}
// sinon tracé de la valeur min ou max de la variable
else
{
// 2 formats de données sont possibles dans le fichier cyloop
if (entetevar.typedon & val_float)
charge_minmax_float (numtrace);
else
charge_minmax_int32 (numtrace);
}
}
}
}
// extraire les valeurs d'une variable de type compteur 16 bits
void charge_val_uint16 (int numtrace)
{
uint16 total; // la donnée
uint16 coef_moy; // coefficient diviseur pour le calcul de moyenne
int instant; // compteur de boucle
// si compteur de passage séparé pour chaque instant du cycle
if (entetevar.typedon & cmoy_sep)
{
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
// lecture du compteur et des données d'un instant du cycle
fread (&coef_moy, 2, 1, descfic);
if (! fread (&total, 2, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// calcul de la donnée pour le graphique
if (coef_moy)
listeval [numtrace][instant] = (float) total / coef_moy;
}
}
// sinon, une valeur coef_moy globale pour tout le cycle
else
{
// prendre en compte la valeur de ce compteur
coef_moy = entetevar.coef_moy;
// terminé si compteur pas encore initialisé (fichier cyloop vierge)
if (! coef_moy)
return;
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
// si on a atteint le dernier instant du cycle avec mise à jour
if (instant == entetevar.der_enreg)
{
// corriger coef_moy pour la fin du cycle
if (entetevar.ponderation)
coef_moy = coef_moy - 256;
else
coef_moy --;
// si coef_moy est arrivé à 0, la suite
// du cycle n'a jamais été parcourue
if (! coef_moy)
return;
}
// lecture de la donnée d'un instant du cycle
if (! fread (&total, 2, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// calcul de la donnée pour le graphique
listeval [numtrace][instant] = (float) total / coef_moy;
}
}
}
// extraire les valeurs d'une variable de type compteur 32 bits
void charge_val_uint32 (int numtrace)
{
uint32 total; // la donnée
uint16 coef_moy; // coefficient diviseur pour le calcul de moyenne
int instant; // compteur de boucle
// si compteur de passage séparé pour chaque instant du cycle
if (entetevar.typedon & cmoy_sep)
{
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
// lecture du compteur et des données d'un instant du cycle
fread (&coef_moy, 2, 1, descfic);
if (! fread (&total, 4, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// calcul de la donnée pour le graphique
if (coef_moy)
listeval [numtrace][instant] = (float) total / coef_moy;
}
}
// sinon, une valeur coef_moy globale pour tout le cycle
else
{
// prendre en compte la valeur de ce compteur
coef_moy = entetevar.coef_moy;
// terminé si compteur pas encore initialisé (fichier cyloop vierge)
if (! coef_moy)
return;
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
// si on a atteint le dernier instant du cycle avec mise à jour
if (instant == entetevar.der_enreg)
{
// corriger coef_moy pour la fin du cycle
if (entetevar.ponderation)
coef_moy = coef_moy - 256;
else
coef_moy --;
// si coef_moy est arrivé à 0, la suite
// du cycle n'a jamais été parcourue
if (! coef_moy)
return;
}
// lecture de la donnée d'un instant du cycle
if (! fread (&total, 4, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// calcul de la donnée pour le graphique
listeval [numtrace][instant] = (float) total / coef_moy;
}
}
}
// extraire les valeurs d'une variable de type entier 32 bits
void charge_val_int32 (int numtrace)
{
int32 total; // la donnée
uint16 coef_moy; // coefficient diviseur pour le calcul de moyenne
int instant; // compteur de boucle
// si compteur de passage séparé pour chaque instant du cycle
if (entetevar.typedon & cmoy_sep)
{
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
// lecture du compteur et des données d'un instant du cycle
fread (&coef_moy, 2, 1, descfic);
if (! fread (&total, 4, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// calcul de la donnée pour le graphique
if (coef_moy)
listeval [numtrace][instant] = (float) total / coef_moy;
// si valeurs min et max mémorisées dans le fichier cyloop
if (entetevar.typedon & mem_min_max)
{
// les sauter
if (fseek (descfic, 8, SEEK_CUR) != 0)
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
}
}
}
// sinon, une valeur coef_moy globale pour tout le cycle
else
{
// prendre en compte la valeur de ce compteur
coef_moy = entetevar.coef_moy;
// terminé si compteur pas encore initialisé (fichier cyloop vierge)
if (! coef_moy)
return;
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
// si on a atteint le dernier instant du cycle avec mise à jour
if (instant == entetevar.der_enreg)
{
// corriger coef_moy pour la fin du cycle
if (entetevar.ponderation)
coef_moy = coef_moy - 256;
else
coef_moy --;
// si coef_moy est arrivé à 0, la suite
// du cycle n'a jamais été parcourue
if (! coef_moy)
return;
}
// lecture de la donnée d'un instant du cycle
if (! fread (&total, 4, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// calcul de la donnée pour le graphique
listeval [numtrace][instant] = (float) total / coef_moy;
// si valeurs min et max mémorisées dans le fichier cyloop
if (entetevar.typedon & mem_min_max)
{
// les sauter
if (fseek (descfic, 8, SEEK_CUR) != 0)
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
}
}
}
}
// extraire les valeurs d'une variable de type flottant
void charge_val_float (int numtrace)
{
float total; // la donnée
uint16 coef_moy; // coefficient diviseur pour le calcul de moyenne
int multip; // pour tenir compte du rapport de 256 éventuel
int instant; // compteur de boucle
// coefficient multiplicateur entre coef_moy et les données flottantes
if (entetevar.ponderation)
multip = 256;
else
multip = 1;
// si compteur de passage séparé pour chaque instant du cycle
if (entetevar.typedon & cmoy_sep)
{
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
// lecture du compteur et des données d'un instant du cycle
fread (&coef_moy, 2, 1, descfic);
if (! fread (&total, 4, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// calcul de la donnée pour le graphique
if (coef_moy)
listeval [numtrace][instant] = (total / coef_moy) * multip;
// si valeurs min et max mémorisées dans le fichier cyloop
if (entetevar.typedon & mem_min_max)
{
// les sauter
if (fseek (descfic, 8, SEEK_CUR) != 0)
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
}
}
}
// sinon, une valeur coef_moy globale pour tout le cycle
else
{
// prendre en compte la valeur de ce compteur
coef_moy = entetevar.coef_moy;
// terminé si compteur pas encore initialisé (fichier cyloop vierge)
if (! coef_moy)
return;
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
// si on a atteint le dernier instant du cycle avec mise à jour
if (instant == entetevar.der_enreg)
{
// corriger coef_moy pour la fin du cycle
if (entetevar.ponderation)
coef_moy = coef_moy - 256;
else
coef_moy --;
// si coef_moy est arrivé à 0, la suite
// du cycle n'a jamais été parcourue
if (! coef_moy)
return;
}
// lecture de la donnée d'un instant du cycle
if (! fread (&total, 4, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// calcul de la donnée pour le graphique
listeval [numtrace][instant] = (total / coef_moy) * multip;
// si valeurs min et max mémorisées dans le fichier cyloop
if (entetevar.typedon & mem_min_max)
{
// les sauter
if (fseek (descfic, 8, SEEK_CUR) != 0)
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
}
}
}
}
// extraire les valeurs min ou max d'une variable de type entier 32 bits
void charge_minmax_int32 (int numtrace)
{
int32 minmax; // la valeur min ou max récupérée
int32 interdite; // valeur interdite pour la variable minmax
int instant; // compteur de boucle
long saut; // saut entre 2 valeur min ou max d'instants successifs
long posit; // pour se positionner dans le fichier cyloop
// octets à sauter entre 2 valeurs min ou max successives
if (entetevar.typedon & cmoy_sep)
saut = 10;
else
saut = 8;
// position de la valeur min ou max du premier instant du cycle
if (typetrace [numtrace] == 'm')
{
posit = saut - 4;
interdite = max_int32;
}
else
{
posit = saut;
interdite = min_int32;
}
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
if (fseek (descfic, posit, SEEK_CUR) != 0)
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
if (! fread (&minmax, 4, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// mémorisation de la valeur récupérée si significative
if (minmax != interdite)
listeval [numtrace][instant] = (float) minmax;
// pour passer au min ou max de l'instant suivant du cycle
posit = saut;
}
}
// extraire les valeurs min ou max d'une variable de type flottant
void charge_minmax_float (int numtrace)
{
float minmax; // la valeur min ou max récupérée
float interdite; // valeur interdite pour la variable minmax
int instant; // compteur de boucle
long saut; // saut entre 2 valeur min ou max d'instants successifs
long posit; // pour se positionner dans le fichier cyloop
// octets à sauter entre 2 valeurs min ou max successives
if (entetevar.typedon & cmoy_sep)
saut = 10;
else
saut = 8;
// position de la valeur min ou max du premier instant du cycle
if (typetrace [numtrace] == 'm')
{
posit = saut - 4;
interdite = max_float;
}
else
{
posit = saut;
interdite = min_float;
}
// calcul de la donnée à utiliser pour chaque instant du cycle
for (instant = 0; instant < entetefic.nb_don_cycle; instant++)
{
if (fseek (descfic, posit, SEEK_CUR) != 0)
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
if (! fread (&minmax, 4, 1, descfic))
// "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
err_fatale ("CYLOOP_TRONQUE");
// mémorisation de la valeur récupérée si significative
if (minmax != interdite)
listeval [numtrace][instant] = minmax;
// pour passer au min ou max de l'instant suivant du cycle
posit = saut;
}
}
// initialisation des couleurs de base de l'image
void initpalette ()
{
memgris (0xFF, coulbase_graph); // couleur de base du graphique
memgris (0xB0, coulbase_image); // couleur de base de l'image
memgris (0xA0, coulbase_quadri); // traits quadrillage
memgris (0x00, coulbase_texte); // couleur du texte
nb_coul = 4; // le numéro de couleur suivant dans la palette
}
// mémorisation d'un niveau de gris dans la palette
void memgris (octet luminosite, octet numcoul)
{
int i; // compteur
for (i = 0; i < 3; i++)
palette [numcoul][i] = luminosite;
palette [numcoul][3] = 0;
}
// mémorisation d'une couleur dans la palette
void memcoul (int32 couleur)
{
if (nb_coul < 16)
{
memcpy (palette [nb_coul], &couleur, 3);
palette [nb_coul++][3] = 0;
}
else
{
// "trop de couleurs demandées pour le graphique"
err_fatale ("TROP_COULEURS");
}
}
// calculer ou corriger la taille du graphique et de l'image globale
void majtaille_image ()
{
// si largeur totale initialisée, l'arrondir au multiple de 8 supérieur
larg_totale = (larg_totale + 7) / 8 * 8;
// calcul de la largeur du graphique si elle n'est pas initialisée
if (larg_graph == 0)
{
if (larg_totale == 0 || larg_totale >= 400)
// largeur par défaut
larg_graph = 400;
else
// largeur limitée à celle de l'image
larg_graph = larg_totale;
}
// calcul de la hauteur du graphique si elle n'est pas initialisée
if (haut_graph == 0)
{
if (haut_totale == 0 || haut_totale >= 100)
// hauteur par défaut
haut_graph = 100;
else
// hauteur limitée à celle de l'image
haut_graph = haut_totale;
}
// si la largeur totale de l'image n'est pas initialisée
if (larg_totale == 0)
// la fixer à 100 pixels de plus que la largeur du graphique
// arrondie au multiple de 8 supérieur
larg_totale = (larg_graph + 107) / 8 * 8;
// si la hauteur totale de l'image n'est pas initialisée
if (haut_totale == 0)
// la fixer (calcul à ajuster en fonction des textes à insérer)
haut_totale = haut_graph + 50;
}
// générer l'image de base (couleur de fond)
void init_image ()
{
int taille_image; // utile suite à un problème de fonctionnment de sizeof
// calcul du nombre d'octets de l'image
taille_image = larg_totale * haut_totale;
// allouer une zone mémoire pour l'image
image = malloc (taille_image);
if (! image)
// "Manque de place mémoire pour générer l'image"
err_fatale ("MANQUE_MEMOIRE2");
// remplir l'image avec la couleur de fond
memset (image, coulbase_image, taille_image);
}
// générer le graphique de base (couleur de fond)
void init_graphique ()
{
int taille_graph; // utile suite à un problème de fonctionnment de sizeof
// calcul du nombre d'octets du graphique
taille_graph = larg_graph * haut_graph;
// allouer une zone mémoire pour le graphique
graphique = malloc (taille_graph);
if (! graphique)
// "Manque de place mémoire pour générer l'image"
err_fatale ("MANQUE_MEMOIRE2");
// remplir le graphique avec la couleur de fond
memset (graphique, coulbase_graph, taille_graph);
}
// positionner le graphique dans l'image
void posit_graphique ()
{
int marge_larg; // espace libre en largeur autour du graphique
int marge_haut; // espace libre en hauteur autour du graphique
// détermination de la position du graphique
marge_larg = larg_totale - larg_graph;
marge_haut = haut_totale - haut_graph;
if (marge_larg > 80)
deb_x = marge_larg - 30;
else
deb_x = marge_larg / 2;
if (marge_haut > 40)
deb_y = marge_haut - 30;
else
deb_y = marge_haut / 2;
}
// tracé de l'échelle sur l'axe x
void gen_echelle_x ()
{
// intervalles normalisés entre 2 traits verticaux de l'échelle
// on utilise des tableaux statiques pour éviter une
// allocation mémoire et initialisation inutiles
// durées des intervalles
static uint32 durees [] =
{ 1, 2, 5, 10, 30, 1, 2, 5, 10, 30, 1, 2, 6, 12, 1, 2,
5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000 };
// échelle de temps en secondes pour ces intervalles
static uint32 echelle [] =
{ 1, 1, 1, 1, 1, 60, 60, 60, 60, 60, 3600, 3600, 3600, 3600,
secjour, secjour, secjour, secjour, secjour, secjour, secjour,
secjour, secjour, secjour, secjour, secjour, secjour };
// durée en jour des 11 premiers mois de l'année (maximum pour février)
static int jourmois [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
uint32 duree_comp; // durée minimale d'un intervalle
uint32 intervalle; // durée réelle d'un intervalle
float som_int; // durée de plusieurs intervalles
int numjour; // numéro de jour dans le mois ou l'année
int mois; // numéro de mois dans l'année
int posit_x; // position du trait vertical généré
int decal_x; // deb_x + décalage pour l'affichage des numéros de mois
int i; // compteur
// si cycle irrégulier
if (entetefic.caract_gen & cycle_ireg)
{
numjour = 0;
// cycle annuel, 1 trait par mois
if (entetefic.caract_gen & cycle_annuel)
{
// initialisation
mois = 1;
decal_x = deb_x + (larg_graph / 24);
aff_entier (mois, deb_y - 5, decal_x, align_centre);
// un trait vertical par mois avec le numéro du mois
while (mois < 12)
{
numjour = numjour + jourmois [mois - 1];
mois ++;
posit_x = trait_vert_quadri (numjour / 366.0);
aff_entier (mois, deb_y - 5, decal_x + posit_x, align_centre);
}
}
// cycle mensuel
else
{
// initialisation
numjour = 1;
aff_entier (numjour, deb_y - 5, deb_x, align_gauche);
// un trait vertical et un numéro de jour tous les 3 jours
while (numjour < 31)
{
numjour = numjour + 3;
posit_x = trait_vert_quadri ((numjour - 1) / 31.0);
aff_entier (numjour, deb_y - 5, deb_x + posit_x, align_gauche);
}
}
}
// sinon, cycle régulier
else
{
// durée minimale d'un intervalle
duree_comp = entetefic.duree_cycle / 12;
// recherche de la durée normalisée immédiatement supérieure
i = 0;
while (durees [i] * echelle [i] < duree_comp)
i++;
// initialisation tracé
intervalle = durees [i] * echelle [i];
som_int = intervalle;
// tracer les traits verticaux du quadrillage et l'échelle
do
{
posit_x = trait_vert_quadri
((float) som_int / entetefic.duree_cycle);
aff_entier (som_int / echelle [i],
deb_y - 5, deb_x + posit_x, align_centre);
som_int += intervalle;
}
while (som_int < entetefic.duree_cycle);
}
}
/*
tracé d'un trait vertical du quadrillage
on pourrait aussi appeler trace_seg, mais cette fonction
effectue un traitement inutilement compliqué
*/
int trait_vert_quadri (float position)
{
int posit_x; // position du trait vertical
octet *adrpix; // adresse d'un pixel du graphique
int i; // compteur
// calcul de la position du trait
posit_x = larg_graph * position;
// initialisation adresse pixel
adrpix = graphique + posit_x;
// tracé du trait
for (i = 0; i < haut_graph; i++)
{
// mettre à jour un pixel
*adrpix = coulbase_quadri;
// descendre d'une ligne
adrpix += larg_graph;
}
return posit_x;
}
// recherche des valeurs min et max pour le tracé du graphique
void rech_minmax ()
{
int i, j; // compteurs
// calcul des valeurs min et max de l'échelle
mingraph = max_float;
maxgraph = min_float;
// pour tous les tracés
for (i = 0; i < nbtrace; i++)
{
// prise en compte des valeurs
for (j = 0; j < entetefic.nb_don_cycle; j++)
{
if (listeval [i][j] < mingraph)
mingraph = listeval [i][j];
if (listeval [i][j] > maxgraph)
maxgraph = listeval [i][j];
}
}
}
// tracé de l'échelle sur l'axe y
void gen_echelle_y ()
{
float max_abs; // plus grande valeur absolue entre mingraph et maxgraph
float intervaprox; // valeur aproximative de l'intervalle
float intervalle; // intervalle sélectionné
float som_int; // somme de plusieurs intervalles
char formatfloat [10]; // format d'un nombre en virgule flottante
int puis10; // puissance de 10 de l'intervalle
float facteur; // facteur multiplicatif dérivé de puis1000
int posit_y; // position du trait horizontal généré
// terminé si fichier cyloop sans données enregistrées
if (mingraph > maxgraph)
return;
// si mingraph et maxgraph tous deux positifs
if (mingraph >= 0)
{
// pour la lisibilité du graphique, il vaut
// mieux passer quelquefois la valeur min à 0
if (mingraph < maxgraph / 2)
mingraph = 0;
max_abs = maxgraph;
}
// sinon si mingraph et maxgraph tous deux négatifs
else if (maxgraph <= 0)
{
if (mingraph / 2 < maxgraph)
maxgraph = 0;
max_abs = -mingraph;
}
// sinon mingraph négatif et maxgraph positif
else
{
// recherche de la plus grande valeur absolue des extrémités du graphe
if (-mingraph > maxgraph)
max_abs = -mingraph;
else
max_abs = maxgraph;
}
// valeur aproximative d'un intervalle
intervaprox = maxgraph / 3;
// cas particulier, toutes les valeurs négatives
if (intervaprox < 0)
intervaprox = - intervaprox;
// recherche d'un intervalle normalisé immédiatement supérieur
intervalle = 5;
puis10 = 0;
// grands intervalles, recherche de la puissance de 10 supérieure
// on fait un 2ème test pour éviter le débordement numérique
while (intervalle < intervaprox && intervalle < (max_float / 10))
{
intervalle = intervalle * 10;
puis10 ++;
}
// petits intervalles, recherche de la puissance de 10 inférieure
while (intervalle > intervaprox)
{
intervalle = intervalle / 10;
puis10 --;
}
// afiner la valeur de l'intervalle du simple au double
intervaprox = intervaprox / 2;
if (intervalle < intervaprox)
{
intervalle = intervalle * 2;
puis10 ++;
if (intervalle < intervaprox)
intervalle = intervalle * 2;
}
// rechercher la valeur du trait le plus bas à tracer
som_int = 0;
while (som_int > mingraph)
som_int = som_int - intervalle;
while (som_int <= mingraph)
som_int = som_int + intervalle;
// déterminer le meilleur format d'affichage de l'échelle
facteur = 1;
// si demande d'affichage d'un pourcentage
// et intervalle ni trop grand ni trop petit
if (optp && (-5 <= puis10) && (intervalle <= 5))
{
// coef multiplicateur
facteur = 100;
if (puis10 > -3)
// affichage d'un pourcentage entier
strcpy (formatfloat, "1.0f %%");
else
// affichage d'un pourcentage à virgule
sprintf (formatfloat, "0.%df %%", -puis10 - 2);
}
// sinon, pas de pourcentage
else
{
// si intervalle entier
if (intervalle >= 1)
{
if (max_abs <= 10000)
// affichage entier
*formatfloat = '\0';
else if (max_abs <= 1E6)
{
// échelle en milliers
if (intervalle < 1000)
strcpy (formatfloat, "1.1fk");
else
strcpy (formatfloat, "1.0fk");
facteur = 0.001;
}
else if (max_abs <= 1E9)
{
// échelle en millions
if (intervalle < 1000000)
strcpy (formatfloat, "1.1fM");
else
strcpy (formatfloat, "1.0fM");
facteur = 0.000001;
}
else
// puissance de 10 fortement positive
strcpy (formatfloat, "1.1E");
}
// sinon intervalle avec valeur décimale
else
{
switch (puis10)
{
case -1 :
case -2 : // affichage d'un nombre à virgule
case -3 : sprintf (formatfloat, "0.%df", -puis10);
break;
// échelle en millièmes
case -4 : strcpy (formatfloat, "1.1fm");
facteur = 1000;
break;
case -5 : // échelle en millionièmes
case -6 : strcpy (formatfloat, "1.0fµ");
facteur = 1000000;
break;
case -7 : strcpy (formatfloat, "1.1fµ");
facteur = 1000000;
break;
// puissance de 10 fortement négative
default : strcpy (formatfloat, "1.1E");
}
}
}
// tracer les traits horizontaux du quadrillage
while (som_int <= maxgraph)
{
posit_y = trait_horiz_quadri (som_int);
// et rajouter l'échelle
if (*formatfloat)
aff_float (som_int * facteur, formatfloat,
posit_y + deb_y + 7, deb_x - 5, align_droite);
else
aff_entier ((long) som_int,
posit_y + deb_y + 7, deb_x - 5, align_droite);
som_int = som_int + intervalle;
}
}
// tracé d'un trait horizontal du quadrillage
int trait_horiz_quadri (float position)
{
int posit_y; // position du trait horizontal
// calcul de la position du trait
posit_y = haut_graph * (position - mingraph) / (maxgraph - mingraph);
// tracé du trait (sauf si en bordure de graphique)
if (posit_y < haut_graph)
memset (graphique + (posit_y * larg_graph),
coulbase_quadri, larg_graph);
return posit_y;
}
// rajouter les tracés dans le graphique
void trace_graphique ()
{
float coef_vert;
int numtrace;
int indice;
int x, y, yprec;
float *liste;
// pour calcul hauteur d'un point sur le graphique
coef_vert = haut_graph / (maxgraph - mingraph);
// tracé du graphique
for (numtrace = 0; numtrace < nbtrace; numtrace++)
{
liste = listeval [numtrace];
// initialisation nécessaire pour 1er point
if (isnan (*liste))
yprec = 0;
else
yprec = (*liste - mingraph) * coef_vert;
// tracé du graphique
for (x = 0; x < larg_graph; x++)
{
// calcul de la position d'un point du graphique
indice = (float) x * entetefic.nb_don_cycle / larg_graph;
// si ce point est défini
if (! isnan (liste [indice]))
{
y = (liste [indice] - mingraph) * coef_vert;
// si tracé sous le forme de trait
if (epaisseur [numtrace] < 255)
{
// faire un trait plus ou moins épais
trace_seg (x, yprec, y, numtrace + 4, epaisseur [numtrace]);
// mémoriser y pour aller au point suivant
yprec = y;
}
// sinon
else
// tracé d'un histogramme
trace_seg (x, 0, y, numtrace + 4, 1);
}
}
}
}
// tracé d'un trait vertical dans le graphique
void trace_seg (int x, int ydeb, int yfin, octet couleur, int epaisseur)
{
octet *adrpix; // adresse d'un pixel du graphique
int ymin, ymax; // valeurs min et max de y
int i; // compteur
// calculer le plus petit et le plus grand entre ydeb et yfin
if (ydeb < yfin)
{
ymin = ydeb;
ymax = yfin;
}
else
{
ymin = yfin;
ymax = ydeb;
}
// prendre en compte l'épaisseur sur les verticales
ymax = ymax + (epaisseur / 2);
ymin = ymin - ((epaisseur - 1) / 2);
// corriger les valeurs de y qui sortiraient du graphique
if (ymin < 0)
ymin = 0;
else if (ymin >= haut_graph)
ymin = haut_graph - 1;
if (ymax >= haut_graph)
ymax = haut_graph - 1;
// initialisation adresse pixel
adrpix = graphique + (ymin * larg_graph) + x;
// tracé du trait
for (i = ymin; i <= ymax; i++)
{
// mettre à jour un pixel
*adrpix = couleur;
// voire plus si traits épais
if (epaisseur - 1)
{
if (x)
*(adrpix - 1) = couleur;
if (epaisseur > 2 && x < larg_graph - 1)
*(adrpix + 1) = couleur;
}
// monter d'une ligne
adrpix += larg_graph;
}
}
// rajouter les commentaires dans l'image
void ajout_textes ()
{
}
// insérer le graphique dans l'image
void insert_graphique ()
{
octet *adrpix_graph; // adresse d'un pixel du graphique
octet *adrpix_image; // adresse d'un pixel de l'image
octet *sauv_adrpix_image; // sauvegarde de adrpix_image
int avant; // compteur de pixels;
int i; // compteur;
// initialisation
sauv_adrpix_image = image + (deb_y * larg_totale) + deb_x;
adrpix_image = sauv_adrpix_image;
adrpix_graph = graphique;
// insertion du graphique dans l'image
for (i = 0; i < haut_graph; i++)
{
// copier une ligne
memcpy (adrpix_image, adrpix_graph, larg_graph);
// passer à la ligne du dessus
adrpix_graph += larg_graph;
adrpix_image += larg_totale;
}
// on va tracer un trait horizontal en dessous de l'image
// pour faire commencer le trait horizontal un peu à gauche de l'image
if (deb_x > deborde_trait)
avant = deborde_trait;
else
avant = deb_x;
// réinitialiser adrpix_image
adrpix_image = sauv_adrpix_image;
// tracé du trait horizontal suivi si possible d'une flèche coté droit
if (deb_y)
{
memset (adrpix_image - larg_totale - avant, coulbase_texte,
larg_graph + avant);
ajout_fleche_droite (deb_x + larg_graph, deb_y - 1);
}
else
memset (adrpix_image - avant, coulbase_texte, larg_graph + avant);
// on va tracer un trait vertical à gauche de l'image
// pour faire commencer le trait vertical un peu en dessous de l'image
if (deb_y > deborde_trait)
avant = deborde_trait;
else
avant = deb_y;
adrpix_image = adrpix_image - (avant * larg_totale);
// positionner le trait vertical au mieux
if (deb_x)
{
// ajout d'une flèche sur le coin bas droit de l'image
ajout_fleche_haut (deb_x - 1, deb_y + haut_graph);
// le trait vertical sera à gauche du graphique
adrpix_image --;
}
// tracé du trait vertical à gauche du graphique
for (i = -avant; i < haut_graph; i++)
{
*adrpix_image = coulbase_texte;
adrpix_image += larg_totale;
}
}
// ajout d'une flèche orientée vers la droite dans l'image
void ajout_fleche_droite (int x, int y)
{
octet *adrpix; // adresse d'un pixel de l'image
int i; // compteur
// la flèche ne sera tracée que s'il y a la place
if (y + 1 < taillefleche)
return;
if (larg_totale - x < taillefleche)
return;
// adresse du point d'encrage de la flèche
adrpix = image + (y * larg_totale) + x;
// trait horizontal central
memset (adrpix, coulbase_texte, taillefleche);
// traits horizontaux de plus en plus courts de part et d'autre
for (i = 1; i < taillefleche; i++)
{
memset (adrpix - (i * larg_totale), coulbase_texte, taillefleche - i);
memset (adrpix + (i * larg_totale), coulbase_texte, taillefleche - i);
}
}
// ajout d'une flèche orientée vers le haut dans l'image
void ajout_fleche_haut (int x, int y)
{
octet *adrpix; // adresse d'un pixel de l'image
int i; // compteur
// la flèche ne sera tracée que s'il y a la place
if (x + 1 < taillefleche)
return;
if (haut_totale - y < taillefleche)
return;
// adresse du coin bas gauche de la flèche
adrpix = image + (y * larg_totale) + x - taillefleche + 1;
// tracé de la flèche
for (i = (taillefleche * 2) - 1; i > 0; i = i - 2)
{
// tracé d'un trait horizontal
memset (adrpix, coulbase_texte, i);
// remonter d'une ligne et avancer d'un pixel
adrpix = adrpix + larg_totale + 1;
}
}
// génération du fichier image
void genficimage (char *nomimage)
{
FILE *descbmp; // descripteur du fichier image BMP
char *imgbmp; // nom du fichier image BMP
char *cmdconvert; // commande de conversion du fichier BMP
entetebmp bmp_entete; // entete du fichier bmp
octet *adrpix; // pointeur sur un pixel de l'image
octet doublepix; // caractère du fichier BMP contenant 2 pixels
int i, j; // compteurs;
// fabrication du nom de fichier image bmp de travail si nécessaire
i = strlen (nomimage) - 1;
while (i > 0 && nomimage [i] != '.')
i--;
// si l'image à générer est une image BMP
if (strcasecmp (nomimage + i, ".bmp") == 0)
// on utilisera juste son nom
imgbmp = nomimage;
// sinon
else
{
// fabriquer un nom d'image BMP de même préfixe
imgbmp = malloc (i + 5);
memcpy (imgbmp, nomimage, i);
strcpy (imgbmp + i, ".bmp");
}
// tentative de création du fichier image BMP
descbmp = fopen (imgbmp, "w");
if (! descbmp)
// "Impossible de créer un fichier image de travail %s"
err_fatale_arg ("IMPOS_CRE_FIMAGE", imgbmp);
// remplissage de l'entête du fichier BMP
// partie invariante pour le type de fichier généré
bmp_entete.bm [0] = 'B';
bmp_entete.bm [1] = 'M';
bmp_entete.zero = 0;
bmp_entete.taillebi = 40;
bmp_entete.un = 1;
bmp_entete.bits_coul = 4;
bmp_entete.compress = 0;
bmp_entete.pixelx_m = 5000;
bmp_entete.pixely_m = 5000;
bmp_entete.coulimport = 0;
// partie qui dépend des caractéristiques de l'image
bmp_entete.couleurs = nb_coul;
bmp_entete.pixels_l = larg_totale;
bmp_entete.pixels_h = haut_totale;
bmp_entete.deplacement = 54 + (4 * nb_coul);
bmp_entete.szimage = (bmp_entete.pixels_l / 2) * bmp_entete.pixels_h;
bmp_entete.taillefic = bmp_entete.szimage + bmp_entete.deplacement;
// copie de l'entête dans le fichier
fwrite (&bmp_entete.bm, sizeof (bmp_entete) - 2, 1, descbmp);
// rajout de la palette dans le fichier
fwrite (palette, 4, nb_coul, descbmp);
// initialisation pointeur
adrpix = image;
// rajout de l'image proprement dite
for (i = 0; i < haut_totale; i++)
{
for (j = 0; j < larg_totale; j = j + 2)
{
// on regroupe 2 pixels consécutifs sur un octet
doublepix = (*adrpix << 4) | *(adrpix + 1);
// qu'on recopie dans le fichier
fputc (doublepix, descbmp);
// avant de passer aux pixels suivants
adrpix = adrpix + 2;
}
}
// image bmp prête
fclose (descbmp);
// si le fichier image à créer n'est pas un fichier BMP
if (imgbmp != nomimage)
{
// détruire la version précédente du fichier image si elle existe
unlink (nomimage);
// générer la commande de conversion du fichier image et l'exécuter
cmdconvert = malloc (strlen (nomimage) + strlen (imgbmp) +10);
sprintf (cmdconvert, "convert %s %s", imgbmp, nomimage);
system (cmdconvert);
// si le fichier image n'a pu être créé
if (access (nomimage, 0) < 0)
{
// messages d'erreur
// "Commande convert de ImageMagick indisponible"
// "Image générée au format BMP dans le fichier %s"
affiche_err ("CONVERT_ABSENTE");
aff_err_arg ("IMAGE_RESTE_BMP", imgbmp);
}
// sinon
else
// on peut détruire le fichier BMP de travail
unlink (imgbmp);
}
}