This repository has been archived on 2023-11-03. You can view files and clone it, but cannot push or open issues or pull requests.
GINPA/Controleur/controleur.c

702 lines
21 KiB
C

#include "controleur.h"
ErrEnum controlePoints(sfRenderWindow *window, const char *cheminFichier, Mode mode)
{
// Ceci est le flag qui sera mis à jour, puis renvoyé à la fin de la fonction
ErrEnum flag;
// La texture de rendus qui va servir tout le long...
sfRenderTexture *const renderTexture = initialisationRenderTexture(sfRenderWindow_getSize(window).x, sfRenderWindow_getSize(window).y);
// Voilà le sprite qui sera utilisé également
sfSprite *renderSprite = NULL;
// Maintenant les variables d'affichage créées, on peut dessiner à l'écran notre écran de chargement...
afficherLoading(window, renderTexture, &renderSprite);
// La fonte qui servira pour l'affichage des dates...
sfFont *const font = loadFontFromFile(BOLDFONTPATH);
// Quelques variables pour les routes et les agglomérats
uint32_t nbElements = 0, nbPointsAffiches = 0, nbAgglosAffiches = 0;
uint32_t nbAgglos = 0, nbPointAgglosMin = 0;
uint32_t nbRoutes = 0, nbAggloGlobaux = 0;
// Les futurs tableaux qui contiendront les points / agglomérats / routes / agglomérats globaux
Point *tabPoints = NULL;
Agglomerat *agglos = NULL;
Route *routes = NULL;
AgglomeratGlobal *aggloGlobaux = NULL;
/* Ci-dessous:
* La variable d'évènements maîtresse ;
* La structure `Rectangle` retournée par la fonction d'attente d'évènements ;
* La variable qui sert de flag entre le Contrôleur et la fonction d'attente d'évènements ;
* Un booléen pour indiquer quand il faut centrer le curseur sur l'écran ;
* Un booléen qui indique si on affiche tous les points, même ceux de type Agglo ;
* Un booléen qui indique s'il faudra vider la pile des vues suivantes.
*/
sfEvent event;
Rectangle carre;
Action action = NO_ACTION;
Tool currentTool = NO_TOOL;
bool centerCursor = false;
bool allPoint = false;
bool allRoads = true;
bool viderPileSuivante;
// Un pointeur sur une carte qui stockera successivement les vues.
Carte *carte = creerCarte(NULL);
// Nos deux piles qui vont gérer les vues successives de l'utilisateur !
PileCartes previous, next;
initialiserPileCartes(&previous);
initialiserPileCartes(&next);
/* HERE START THE OPERATIONS */
// On vient lire les logs de géolocalisation
flag = loadLogs(cheminFichier, &tabPoints, &nbElements);
if(flag != LOAD_SUCCESS)
{
if(flag == CORRUPTED_FILE)
{
warningBox(window, renderSprite, "Impossible d\'ouvrir ce fichier, est-il au bon format ?\n\nFormat attendu:\t\'date:tttttttttt,lat:xx.xxxxxx,long:yy.yyyyyy;\'");
}
return flag;
}
//On initilise toutes les structures et met à jour les types et les pointeurs des points.
flag = initStructures(tabPoints, nbElements, &agglos, &nbAgglos, &routes, &nbRoutes, &aggloGlobaux, &nbAggloGlobaux);
if(flag != SUCCESS)
{
return flag;
}
// Deux petits tableaux pour sauvegarder l'adresse des points et agglomérats affichés en mémoire
sfCircleShape** tabPointeursCercles = calloc(nbElements, sizeof(sfCircleShape*));
sfCircleShape** tabPointeursCerclesAgglos = calloc(nbAgglos, sizeof(sfCircleShape*));
sfVertexArray **tabPointeursArray = calloc(nbRoutes, sizeof(sfVertexArray*));
// On lance la procédure de conversion des dates
char **tabDatesConverties = conversionDates(tabPoints, nbElements);
if(tabDatesConverties == NULL)
{
free(tabPoints);
free(agglos);
free(routes);
free(aggloGlobaux);
free(tabPointeursCercles);
free(tabPointeursCerclesAgglos);
free(tabPointeursArray);
sfFont_destroy(font);
detruirePileCartes(&next);
detruirePileCartes(&previous);
return ALLOCATION_FAILURE;
}
// Lors de la première itération, on récupère les positions extrêmes des points
getExtrema(tabPoints, nbElements, &carte->pointHautGauche, &carte->pointBasDroite);
// ... puis le point central le plus adapté
calculPointCentral(carte);
// ... et enfin on calcule le zoom et l'échelle ad hoc
getZoomEchelle(carte);
bool keepGoing = true;
do
{
// Il est temps de récupérer la-dite carte !
if(!getBackgroundMap(carte))
{
warningBox(window, renderSprite, "Une erreur est survenue durant le téléchargement de la carte. Êtes-vous connecté à Internet ?");
flag = REQUEST_FAILURE;
break;
}
// On convertit les points en coordonnées cartésiennes !
conversionPoints(tabPoints, nbElements, &carte->pointCentral, carte->echelle);
// On convertit les agglos en coordonnées cartésiennes !
conversionAgglos(agglos, nbAgglos, &carte->pointCentral, carte->echelle);
// On affiche la carte !
afficherCarte(renderTexture, &carte->mapTexture, &carte->mapSprite, MAPFILE);
// Sert à indiquer si l'utilisateur pourra zoomer ou non (au clic) sur la carte
action = (carte->zoom < ZOOMAX ? ACTION_ZOOMABLE : ACTION_UNZOOMABLE);
if(mode == MODE_GLOBAL || mode == MODE_PREVIEW || mode == MODE_PREVIEW_SUGG || mode == MODE_PBP_AUTOMATIC || mode == MODE_PBP_MANUAL)
{
nbPointAgglosMin = getMinAgglo(agglos, nbAgglos);
// Gestion du menu
Menu menu;
initialiserMenu(&menu);
if(mode == MODE_PBP_AUTOMATIC || mode == MODE_PBP_MANUAL)
{
updateAndDrawMenu(renderTexture, &menu, mode, false, true, currentTool);
}
Point **ptsAffiches = affichageLogs(window, renderTexture, tabPoints, nbElements, &nbPointsAffiches, nbPointAgglosMin, tabPointeursCercles, agglos, nbAgglos, routes, nbRoutes, tabPointeursArray, tabPointeursCerclesAgglos, &nbAgglosAffiches, allPoint, allRoads, carte, mode, &menu);
if(ptsAffiches == NULL)
{
free(tabPoints);
free(agglos);
free(routes);
free(aggloGlobaux);
free(tabPointeursCercles);
free(tabPointeursCerclesAgglos);
free(tabPointeursArray);
freeTabGeneric((void***)&tabDatesConverties, nbElements);
sfFont_destroy(font);
detruirePileCartes(&next);
detruirePileCartes(&previous);
if(nbPointsAffiches == 0 && nbAgglosAffiches == 0)
{
return SUCCESS;
}
else
{
return ALLOCATION_FAILURE;
}
}
// On tente un truc: Si l'affichage des logs est terminé, on passe en mode global :O
if(mode == MODE_PBP_AUTOMATIC || mode == MODE_PBP_MANUAL)
{
mode = MODE_GLOBAL;
afficherSablier(window, renderTexture, &renderSprite);
libererMemoireElementsAffiches(carte, nbPointsAffiches, tabPointeursCercles, nbAgglosAffiches, tabPointeursCerclesAgglos, nbRoutes, tabPointeursArray);
detruireMenu(&menu);
renderSprite = NULL;
free(ptsAffiches);
ptsAffiches = NULL;
continue;
}
// Si l'utilisateur a (dé)zoomé, on re-centre son curseur au milieu de l'écran juste avant de lui rendre la main !
if(centerCursor)
{
sfMouse_setPosition((sfVector2i){WIDTH / 2, HEIGHT / 2}, (sfWindow*)window);
centerCursor = false;
}
updateAndDrawMenu(renderTexture, &menu, mode, true, true, currentTool);
freeSprite(&renderSprite);
renderSprite = loadSpriteFromRenderTexture(renderTexture);
displayBackgroundSprite(window, renderSprite);
carre = waitingOnMapEvent(window, &event, renderTexture, renderSprite, carte, nbPointsAffiches, ptsAffiches, tabPointeursCercles, nbAgglos, tabPointeursCerclesAgglos, agglos, nbElements, tabPoints, tabDatesConverties, font, &action, mode, &menu, &currentTool);
detruireMenu(&menu);
// Nous pouvons remettre ce sprite à `NULL` car il a été (normalement) correctement libéré dans la fonction d'attente ci-dessus !
renderSprite = NULL;
free(ptsAffiches);
ptsAffiches = NULL;
}
else
{
warningBox(window, renderSprite, "Le mode renseigné par le DÉVELOPPEUR n\'est pas correct :P.\n");
flag = MODE_FAILURE;
break;
}
// Action par défaut...
viderPileSuivante = false;
switch(action)
{
case ACTION_QUIT:
// L'utilisateur a décidé de quitter cette vue de la carte !
flag = SUCCESS;
keepGoing = false;
break;
case ACTION_REFRESH:
break;
case ACTION_ZOOM_IN:
case ACTION_ZOOM_OUT:
case ACTION_ZOOM_ZONE:
viderPileSuivante = true;
afficherSablier(window, renderTexture, &renderSprite);
// On empile cette carte dans la pile des cartes précédentes
if(!empilerPileCartes(&previous, carte))
{
afficherWarningPile(window, renderSprite);
break;
}
// On crée une nouvelle carte pour la vue demandée (qui possèdera les mêmes champs que la carte actuelle [cf. la documentation de la fonction])
carte = creerCarte(carte);
if(action == ACTION_ZOOM_ZONE)
{
/* ZOOM ZONE (CLIC) */
// On convertit les pixels retournés en coordonnées géographiques...
conversionRec(&carre, &carte->pointHautGauche, &carte->pointBasDroite, &carte->pointCentral, carte->echelle);
// On calcule le zoom et l'échelle adaptés à la nouvelle zone
getZoomEchelle(carte);
// ainsi qu'un nouveau point central !
calculPointCentral(carte);
}
else
{
/* ZOOMS MOLETTE */
// En fonction du sens de rotation de la molette (renvoyé par `waitingOnMapEvent`, on agrandit ou diminue le zoom)
carte->zoom += (action == ACTION_ZOOM_IN ? 1 : -1);
// On convertit la position du curseur en coordonnées GPS, et on stocke les valeurs dans la structure représentant (le nouveau) point central de la carte
conversionPixelsCoordonnees(&carte->pointCentral, &(sfVector2f){(double)event.mouseWheel.x, (double)event.mouseWheel.y}, &carte->pointCentral, carte->echelle);
carte->echelle = getEchelleFromZoom(carte->zoom);
// Pour ne pas dérouter l'utilisateur pendant son (dé)zoom, on centrera son cuseur au milieu de la carte !
centerCursor = true;
}
break;
case ACTION_ZOOM_NEXT:
case ACTION_ZOOM_PREV:
// L'utilisateur a demandé d'afficher la vue précédente / suivante. Donc on commence par empiler la vue actuelle dans la pile des suivantes / précédentes.
if(!empilerPileCartes((action == ACTION_ZOOM_NEXT ? &previous : &next), carte))
{
afficherWarningPile(window, renderSprite);
break;
}
// La prochaine vue correspond donc à la dernière empilée dans les suivantes / précédentes
carte = depilerPilesCartes((action == ACTION_ZOOM_NEXT ? &next : &previous));
// Gestion de la pile vide: On remet la vue que l'on a précédemment empilée (avantage: aucune variable temporaire)
if(carte == NULL)
{
carte = depilerPilesCartes((action == ACTION_ZOOM_NEXT ? &previous : &next));
}
else
{
// ... la pile n'est pas vide = il va y avoir un changement de carte = on affiche le sablier pendant le chargement !
afficherSablier(window, renderTexture, &renderSprite);
}
break;
case ACTION_CHANGE_MAP_TYPE:
afficherSablier(window, renderTexture, &renderSprite);
if(++carte->type == LASTYPE)
{
carte->type = FIRSTYPE + 1;
}
break;
case ACTION_CHANGE_VISUALISATION:
if(mode == MODE_GLOBAL)
{
mode = MODE_PREVIEW;
}
else if(mode == MODE_PREVIEW)
{
mode = MODE_PREVIEW_SUGG;
}
else if(mode == MODE_PREVIEW_SUGG)
{
mode = MODE_GLOBAL;
}
break;
case ACTION_CENTERPOSITION:
viderPileSuivante = true;
afficherSablier(window, renderTexture, &renderSprite);
if(!empilerPileCartes(&previous, carte))
{
afficherWarningPile(window, renderSprite);
break;
}
carte = creerCarte(NULL);
getExtrema(tabPoints, nbElements, &carte->pointHautGauche, &carte->pointBasDroite);
calculPointCentral(carte);
getZoomEchelle(carte);
break;
case ACTION_REPEAT:
break;
case ACTION_TOGGLEAGGLO:
afficherSablier(window, renderTexture, &renderSprite);
allPoint = !allPoint;
break;
case ACTION_TOGGLEROUTE:
afficherSablier(window, renderTexture, &renderSprite);
allRoads = !allRoads;
break;
default:
errorBox(window, "Erreur fatale.\n");
}
if(viderPileSuivante)
{
detruirePileCartes(&next);
initialiserPileCartes(&next);
}
libererMemoireElementsAffiches(carte, nbPointsAffiches, tabPointeursCercles, nbAgglosAffiches, tabPointeursCerclesAgglos, nbRoutes, tabPointeursArray);
} while(keepGoing); // Ceci est bien entendu une fausse boucle infinie (cf. le test juste ci-dessus...)
detruirePileCartes(&next);
detruirePileCartes(&previous);
sfFont_destroy(font);
free(tabPoints);
free(agglos);
free(routes);
free(aggloGlobaux);
freeTabGeneric((void***)&tabDatesConverties, nbElements);
free(tabPointeursCercles);
free(tabPointeursCerclesAgglos);
free(tabPointeursArray);
return flag;
}
ErrEnum controleMultipleLogs(sfRenderWindow *const window, char selectedFiles[MAXSELECTEDFILES][MAXPATHLENGTH], const uint16_t nbFiles)
{
ErrEnum flag;
sfRenderTexture *const renderTexture = initialisationRenderTexture(sfRenderWindow_getSize(window).x, sfRenderWindow_getSize(window).y);
sfSprite *renderSprite = NULL;
sfFont *font = loadFontFromFile(BOLDFONTPATH);
afficherLoading(window, renderTexture, &renderSprite);
uint32_t sommeNbElements = 0;
Point *tabTabPoints[nbFiles];
uint32_t tabNbPoints[nbFiles];
Agglomerat *tabAgglos[nbFiles];
uint32_t tabNbAgglos[nbFiles];
Agglomerat *tabAggloTotal = NULL;
uint32_t tailleTabAggloTotal = 0;
AgglomeratGlobal *globalMultiFile = NULL;
uint32_t tailleGlobalMultiFile = 0;
// Pour chaque fichier de logs, on vient charger les éléments en mémoire un à un...
for(uint16_t i = 0; i < nbFiles; i++)
{
flag = loadLogs(selectedFiles[i], &tabTabPoints[i], &tabNbPoints[i]);
if(flag != LOAD_SUCCESS)
{
for(int16_t j = i - 1; j >= 0; j--)
{
free(tabTabPoints[j]);
}
return flag;
}
sommeNbElements += tabNbPoints[i];
flag = repereAgglo(tabTabPoints[i], tabNbPoints[i], &tabAgglos[i], &tabNbAgglos[i]);
if(flag != SUCCESS)
{
for(int16_t j = i; j >= 0; j--)
free(tabTabPoints[j]);
for(int16_t j = i - 1; j >= 0; j--)
free(tabAgglos[i]);
return flag;
}
}
/* Chaque fichier de log est chargé => réunion des tableaux d'agglos en un seul */
// On compte combien il y a d'agglos
for(uint32_t i = 0; i < nbFiles; ++i)
tailleTabAggloTotal += tabNbAgglos[i];
// Le calloc des familles pas très propre
tabAggloTotal = calloc(tailleTabAggloTotal, sizeof(*tabAggloTotal));
if(tabAggloTotal == NULL)
{
fprintf(stderr, "%s\n", strerror(errno));
for(uint16_t i = 0; i < nbFiles; i++)
free(tabTabPoints[i]);
for(uint16_t i = 0; i < nbFiles; i++)
free(tabAgglos[i]);
return ALLOCATION_FAILURE;
}
for(uint32_t i = 0, k = 0; i < nbFiles; ++i)
{
for(uint32_t j = 0; j < tabNbAgglos[i]; ++j, ++k)
{
tabAggloTotal[k] = tabAgglos[i][j];
}
free(tabAgglos[i]); // <-- À surveiller
}
flag = assemblageGlobalAgglo(tabAggloTotal, tailleTabAggloTotal, &globalMultiFile, &tailleGlobalMultiFile);
if(flag != SUCCESS)
{
free(tabAggloTotal);
for(uint16_t i = 0; i < nbFiles; i++)
free(tabTabPoints[i]);
return flag;
}
// Une variable de type carte pour stocker quelques infos sur notre vue actuelle...
Carte *carte = creerCarte(NULL);
// Un tableau pour sauvegarder l'adresse des cercles dessinés à l'écran
sfCircleShape** tabPointeursCerclesAgglos = calloc(tailleGlobalMultiFile, sizeof(sfCircleShape*));
uint32_t nbPointAgglosAffiches = 0;
Point *positionAgglos = calloc(tailleGlobalMultiFile, sizeof(*positionAgglos));
if(positionAgglos == NULL)
{
free(globalMultiFile);
free(tabAggloTotal);
for(uint16_t i = 0; i < nbFiles; i++)
free(tabTabPoints[i]);
return ALLOCATION_FAILURE;
}
// On copie les coordonnées de ces agglomérats dans un tableau de Points (pour utiliser les fonctions déjà implémentées)
for(uint16_t i = 0; i < tailleGlobalMultiFile; i++)
{
positionAgglos[i].pos = globalMultiFile[i].moy;
// On profite de cette itération pour charger en mémoire les adresses des agglomérats
// Elle tient sur une seule ligne, mais c'est une opération très importante.
char *temp = getReverseGeocoding(&globalMultiFile[i].moy);
globalMultiFile[i].adresse = (temp != NULL ? getRidOfAccents(temp) : NULL);
}
getExtrema(positionAgglos, tailleGlobalMultiFile, &carte->pointHautGauche, &carte->pointBasDroite);
calculPointCentral(carte);
getZoomEchelle(carte);
/* Variables de contrôleur */
Menu menu;
initialiserMenu(&menu);
PileCartes previous, next;
initialiserPileCartes(&previous);
initialiserPileCartes(&next);
sfEvent event;
bool viderPileSuivante;
bool centerCursor = false;
Action action = NO_ACTION;
Tool currentTool = NO_TOOL;
bool keepGoing = true;
do
{
if(!getBackgroundMap(carte))
{
warningBox(window, renderSprite, "Une erreur est survenue durant le téléchargement de la carte. Êtes-vous connecté à Internet ?");
flag = REQUEST_FAILURE;
break;
}
conversionPoints(positionAgglos, tailleGlobalMultiFile, &carte->pointCentral, carte->echelle);
// Une fois les conversions faites, on repasse dans l'autre sens !
for(uint16_t i = 0; i < tailleGlobalMultiFile; i++)
{
globalMultiFile[i].moyConv = positionAgglos[i].posConv;
}
afficherCarte(renderTexture, &carte->mapTexture, &carte->mapSprite, MAPFILE);
const uint32_t nbPointAgglosMin = getMinAggloGlobal(globalMultiFile, tailleGlobalMultiFile);
for(uint16_t i = 0; i < tailleGlobalMultiFile; i++)
{
if(pointOnMap(&globalMultiFile[i].moyConv))
{
dessinerAggloGlobal(renderTexture, &globalMultiFile[i], &tabPointeursCerclesAgglos[i], nbPointAgglosMin, carte, COULEUR_AGGLO);
nbPointAgglosAffiches++;
}
}
updateAndDrawMenu(renderTexture, &menu, MODE_MULTI_FILES, false, false, currentTool);
if(centerCursor)
{
sfMouse_setPosition((sfVector2i){WIDTH / 2, HEIGHT / 2}, (sfWindow*)window);
centerCursor = false;
}
freeSprite(&renderSprite);
renderSprite = loadSpriteFromRenderTexture(renderTexture);
displayBackgroundSprite(window, renderSprite);
action = (carte->zoom < ZOOMAX ? ACTION_ZOOMABLE : ACTION_UNZOOMABLE);
Rectangle carre = waitingOnMapEventAgglo(window, &event, renderTexture, renderSprite, carte, tailleGlobalMultiFile, globalMultiFile, tabPointeursCerclesAgglos, font, &action, &menu, &currentTool);
renderSprite = NULL;
viderPileSuivante = false;
switch(action)
{
case ACTION_QUIT:
flag = SUCCESS;
keepGoing = false;
break;
case ACTION_ZOOM_IN:
case ACTION_ZOOM_OUT:
case ACTION_ZOOM_ZONE:
viderPileSuivante = true;
afficherSablier(window, renderTexture, &renderSprite);
if(!empilerPileCartes(&previous, carte))
{
afficherWarningPile(window, renderSprite);
break;
}
carte = creerCarte(carte);
if(action == ACTION_ZOOM_ZONE)
{
conversionRec(&carre, &carte->pointHautGauche, &carte->pointBasDroite, &carte->pointCentral, carte->echelle);
getZoomEchelle(carte);
calculPointCentral(carte);
}
else
{
carte->zoom += (action == ACTION_ZOOM_IN ? 1 : -1);
conversionPixelsCoordonnees(&carte->pointCentral, &(sfVector2f){(double)event.mouseWheel.x, (double)event.mouseWheel.y}, &carte->pointCentral, carte->echelle);
carte->echelle = getEchelleFromZoom(carte->zoom);
centerCursor = true;
}
break;
case ACTION_ZOOM_NEXT:
case ACTION_ZOOM_PREV:
if(!empilerPileCartes((action == ACTION_ZOOM_NEXT ? &previous : &next), carte))
{
afficherWarningPile(window, renderSprite);
break;
}
carte = depilerPilesCartes((action == ACTION_ZOOM_NEXT ? &next : &previous));
if(carte == NULL)
{
carte = depilerPilesCartes((action == ACTION_ZOOM_NEXT ? &previous : &next));
}
else
{
afficherSablier(window, renderTexture, &renderSprite);
}
break;
case ACTION_CHANGE_MAP_TYPE:
afficherSablier(window, renderTexture, &renderSprite);
if(++carte->type == LASTYPE)
{
carte->type = FIRSTYPE + 1;
}
break;
case ACTION_CENTERPOSITION:
viderPileSuivante = true;
afficherSablier(window, renderTexture, &renderSprite);
if(!empilerPileCartes(&previous, carte))
{
afficherWarningPile(window, renderSprite);
break;
}
carte = creerCarte(NULL);
getExtrema(positionAgglos, tailleGlobalMultiFile, &carte->pointHautGauche, &carte->pointBasDroite);
calculPointCentral(carte);
getZoomEchelle(carte);
break;
case ACTION_REPEAT:
break;
default:
errorBox(window, "Erreur fatale.\n");
}
if(viderPileSuivante)
{
detruirePileCartes(&next);
initialiserPileCartes(&next);
}
libererMemoireElementsAffichesAgglos(carte, tailleGlobalMultiFile, tabPointeursCerclesAgglos);
nbPointAgglosAffiches = 0;
} while(keepGoing);
detruireMenu(&menu);
detruirePileCartes(&next);
initialiserPileCartes(&next);
sfFont_destroy(font);
freeSprite(&renderSprite);
sfRenderTexture_destroy(renderTexture);
for(uint16_t i = 0; i < tailleGlobalMultiFile; i++)
{
free(globalMultiFile[i].adresse);
}
free(globalMultiFile);
free(tabAggloTotal);
for(uint16_t i = 0; i < nbFiles; i++)
free(tabTabPoints[i]);
free(positionAgglos);
free(tabPointeursCerclesAgglos);
return flag;
}