Sphinx-Chart2

Sphinx-Chart2

Comme tous mes plugins, ce script est publié sous licence CC BY 4.0.

Pourquoi Chart2 ? Parce qu’il y a un petit moment maintenant, j’avais fait une passerelle pour pouvoir utiliser Chart.js dans RPG Maker MV, disponible sur le forum rpgmakervx-fr.com, et je l’avais appelé Sphinx-Chart, tout simplement.

Toutefois, cette passerelle est loin d’être sans défaut. Aussi, j’y repensais et j’ai décidé de faire une version 2.0. Chart.js nécessite un canvas et son contexte 2D pour être utilisé. Et il se trouve que les sprites ont justement un attribut de type contexte 2D de canvas. J’ai donc tenté de passer ce canvas et ce contexte à cette librairie. Mais j’ai obtenu une erreur.

Ne me désarmant pas, j’ai décidé de passer outre, et, laissant derrière moi Chart.js ; qui est malgré tout une très bonne librairie, juste inadaptée pour RPG Maker MV ; j’ai décidé de coder des graphiques dans des sprites. Ce n’était pas chose aisée. Pas que ca ait été trop complexe, juste fastidieux en fait.

Et voilà le résultat : Sphinx-Chart2 !

Et après ?

Ce système s’appuie sur 3 scripts javascript. Le premier, c’est un script plus ou moins fourre-tout où j’enregistre les fonctions qui me servent dans plusieurs plugins, disponible ici : https://genie23.fr/js-rmmv/sphinx-polyfill/.

Ensuite, vous aurez besoin des définitions des différentes fonctions d’accélération. Je les ai réunies dans un sous objet de la classe statique Math, Math.easing, disponible ici : https://genie23.fr/js-rmmv/sphinx-matheasing/.

Ce script contient 22 courbes d’accélération pour animer différement l’ouverture des graphiques. Vous pouvez bien entendu en créer autant d’autres que vous le souhaitez, en les ajoutant à la sous classe Math.easing, elles seront utilisables automatiquement par les graphiques.

Et pour terminer, on va rentrer dans le vif du sujet. On est là pour parler graphiques, alors parlons graphiques :

//=============================================================================
// Sphinx-Chart2.js
//=============================================================================

/*:
 * @plugindesc Graphique sous forme de sprite
 * @author Sphinx
 * @help
 * //==========================================================================
 * // Plugin : Sphinx-Chart2
 * // Date   : 29 décembre 2019
 * // Auteur : Sphinx
 * //==========================================================================
 * Ce plugin ne contient aucune commande de plugin. Il met à disposition des
 * scripteurs une nouvelle classe de sprite représentant des graphiques.
 * Créer un graphique :
 * variable = new SphinxChart2(
 *     type: TYPE_GRAPHIQUE,
 *     title: TITRE_GRAPHIQUE,
 *     categoriesLabels: LABELS_DE_CATEGORIES,
 *     xAxisLabels: LABELS_DES_ABSCISSES,
 *     datas: DONNEES,
 *     colors: COULEURS,
 *     options: OPTIONS
 * );
 * où :
 *      TYPE_GRAPHIQUE est l'un des types suivants :
 *          - line
 *          - bar
 *          - radar
 *          - pie
 *          - doughnut
 *      TITRE_GRAPHIQUE est une chaine de caractères qui sera affichée au
 *          dessus du graphique
 *      LABELS_DE_CATEGORIES est un tableau contenant la liste des étiquettes
 *          des légendes à afficher
 *      LABELS_DES_ABSCISSES est un tableau contenant la liste des étiquettes
 *          de l'axe des abscisses à afficher
 *      DONNEES est un tableau à 2 dimensions contenant les données à afficher
 *          Pour les graphiques pie et doughnut, utiliser un tableau à 1
 *          dimension
 *      COULEURS Liste des couleurs des données
 *      OPTIONS est un objet de configuration du graphique contenant les clés
 *          suivantes :
 *          - easing : méthode d'animation de la classe Math.easing
 *          - duration : durée de l'animation à l'ouverture du graphique
 *          - forceMinToZero: true - le graphique commencera forcément à 0
 *                            false - configuration par défaut
 *              Inutilisé par les graphiques pie et doughnut
 *          - background : couleur(s) d'arrière plan. Chaine de caractères pour
 *              une couleur unie, tableau de chaines de caractères pour un
 *              dégradé, un objet Bitmap pour une image de fond
 *          - position : objet placant le graphique contenant les clés
 *              suivantes :
 *              - x : coordonnée X
 *              - y : coordonnée Y
 *              - width : largeur
 *              - height : hauteur
 *          - padding : objet définissant les marges de chaque côté contenant
 *              les clés suivantes :
 *              - top : marge en haut du graphique
 *              - left : marge à gauche du graphique
 *              - right : marge à droite du graphique
 *              - bottom : marge en bas du graphique
 * 
 * Ensuite, il suffit de l'ajouter à la scène ou à une window comme un sprite
 * ordinaire.
 * 
 * Exemple de graphique :
 * chart = new SphinxChart2({
 *     type: "line",
 *     title: "Test",
 *     categoriesLabels: [
 *         "Category 1",
 *         "Category 2",
 *         "Category 3"
 *     ],
 *     xAxisLabels: [
 *         "1", "2", "3", "4", "5"
 *     ],
 *     datas: [
 *         [ 5, 2, 1, 4, 3 ],
 *         [ 50, 21, 18, 34, 16 ],
 *         [ 8, 5, 3, 7, 9 ],
 *     ],
 *     colors: [
 *         "#0099FF",
 *         "#FF3300",
 *         "#00CC00",
 *         "#CC3399",
 *         "#FF9933",
 *         "#99CC00",
 *         "#009999",
 *         "#CC99FF"
 *     ],
 *     options: {
 *         easing: "easeOutQuart",
 *         duration: 1,
 *         background: "transparent",
 *         forceMinToZero: true,
 *         position: {
 *             x: 50,
 *             y: 50,
 *             width: 640,
 *             height: 480,
 *         },
 *         padding: {
 *             top: 0,
 *             left: 25,
 *             right: 25,
 *             bottom: 25,
 *         }
 *     }
 * });
 * 
 *     - Dépendances : Sphinx-Polyfill
 *                     Sphinx-MathEasing
 */
DEFAULT_COLORS = [
    "DeepPink", "DodgerBlue", "Gold", "OrangeRed", "WhiteSmoke", "DarkViolet"
];

function SphinxChart2() {
    this.initialize.apply(this, arguments);
};

SphinxChart2.prototype = Object.create(Sprite_Button.prototype);
SphinxChart2.prototype.constructor = SphinxChart2;

SphinxChart2.prototype.initialize = function(config) {
    Sprite_Button.prototype.initialize.call(this);
    
    // Configuration par défaut
    this.config = config || {};
    this.config.type = config.type || "line";
    this.config.title = config.title || "";
    this.config.categoriesLabels = config.categoriesLabels || [];
    this.config.xAxisLabels = config.xAxisLabels || [];
    this.config.datas = config.datas || [];
    this.config.colors = config.colors || DEFAULT_COLORS;
    this.config.options = config.options || {};
    this.config.options.easing = config.options.easing || "linearTween";
    this.config.options.duration = config.options.duration || 1;
    this.config.options.background = config.options.background || "transparent";
    this.config.options.forceMinToZero = config.options.forceMinToZero || false;
    this.config.options.position = config.options.position || {};
    this.config.options.position.x = config.options.position.x || 0;
    this.config.options.position.y = config.options.position.y || 0;
    this.config.options.position.width = config.options.position.width || 0;
    this.config.options.position.height = config.options.position.height || 0;
    this.config.options.padding = config.options.padding || {};
    this.config.options.padding.top = config.options.padding.top || 0;
    this.config.options.padding.left = config.options.padding.left || 0;
    this.config.options.padding.right = config.options.padding.right || 0;
    this.config.options.padding.bottom = config.options.padding.bottom || 0;
    
    // Taille et position du sprite
    this.x = this.config.options.position.x;
    this.y = this.config.options.position.y;
    this.width = this.config.options.position.width;
    this.height = this.config.options.position.height;
    
    // Récupération du contexte de dessin et initialisation du compteur de frames
    this.frame = 0;
    this.bitmap = new Bitmap(this.width, this.height);
    this.context = this.bitmap._context;
    
    // Gestion du clic
    this.setClickHandler(SphinxChart2.prototype.onTouch.bind(this));
};

SphinxChart2.prototype.update = function() {
    Sprite_Button.prototype.update.call(this);
    
    // Mise à jour du graphisme
    this.drawFrame();
    
    // Mise à jour du compteur de frames
    ++this.frame;
};

SphinxChart2.prototype.onTouch = function() {
    // Mise à jour du compteur de frames
    this.frame = 0;
};

SphinxChart2.prototype.drawFrame = function() {
    // Si l'animation est finie, on sort
    if(this.frame > this.config.options.duration * 60) return;
    
    // Nettoyage du cache
    this.context.clearRect(0, 0, this.width, this.height);
    
    // Dessin de l'arrière plan
    if(this.config.options.background instanceof Bitmap) {
        backgroundResize = {};
        if(this.config.options.background.width / this.config.options.background.height > this.width / this.height) {
            backgroundResize.width = this.width;
            backgroundResize.height = this.width / (this.config.options.background.width / this.config.options.background.height);
            backgroundResize.top = (this.height - backgroundResize.height) / 2;
            backgroundResize.left = 0;
        } else {
            backgroundResize.width = this.height * this.config.options.background.width / this.config.options.background.height;
            backgroundResize.height = this.height;
            backgroundResize.top = 0;
            backgroundResize.left = (this.width - backgroundResize.width) / 2;
        }
        this.bitmap.blt(this.config.options.background, 0, 0, this.config.options.background.width, this.config.options.background.height, backgroundResize.left, backgroundResize.top, backgroundResize.width, backgroundResize.height);
    } else if(this.config.options.background instanceof Array && this.config.options.background.length > 1) {
        gradient = this.context.createLinearGradient(0, 0, 0, this.height);
        gradient.addColorStop(0, this.config.options.background[0]);
        for(i = 1; i < this.config.options.background.length; ++i) {
            gradient.addColorStop(i / (this.config.options.background.length - 1), this.config.options.background[i]);
        }
        this.context.fillStyle = gradient;
        this.context.fillRect(0, 0, this.width, this.height);
    } else if(this.config.options.background instanceof Array) {
        this.context.fillStyle = this.config.options.background[0];
        this.context.fillRect(0, 0, this.width, this.height);
    } else {
        this.context.fillStyle = this.config.options.background;
        this.context.fillRect(0, 0, this.width, this.height);
    }
    
    // Calcul de la hauteur et écriture du titre
    if(this.config.title.length > 0) {
        titleHeight = this.bitmap.measureTextHeight(this.config.title);
        this.bitmap.fontSize = 28;
        this.context.font = this.bitmap._makeFontNameText();
        this.context.textAlign = "center";
        this.context.fillStyle = "black";
        this.context.fillText(this.config.title, this.width / 2, titleHeight + this.config.options.padding.top, this.width);
    }
    
    // Math.easing[this.config.options.easing](this.frame, 50, 285, this.config.options.duration * 60);
    legendPosition = "bottom";
    switch(this.config.type) {
        case "line":
            this.drawLineFrame();
            break;
        case "bar":
            this.drawBarFrame();
            break;
        case "radar":
            this.drawRadarFrame();
            break;
        case "pie":
            legendPosition = "right";
            this.drawPieFrame();
            break;
        case "doughnut":
            legendPosition = "right";
            this.drawDoughnutFrame();
            break;
    }
    
    // Calcul de la largeur des légendes
    legendsWidth = 0;
    for(category of this.config.categoriesLabels) {
        legendsWidth += 60 + this.bitmap.measureTextWidth(category);
    }
    
    // Ecriture des légendes
    legendsLeft = (this.width - legendsWidth) / 2
    for(i = 0; i < this.config.categoriesLabels.length; ++i) {
        // Nom de la catégorie
        category = this.config.categoriesLabels[i];
        
        // Ecriture de la couleur (rectangle)
        this.context.save();
        this.context.globalAlpha = 0.59765625;
        this.context.fillStyle = this.config.colors[i % this.config.colors.length];
        this.context.fillRect(legendsLeft, this.height - categoriesHeight, 30, categoriesHeight - 5);
        this.context.restore();
        this.context.lineWidth = 2;
        this.context.strokeStyle = this.config.colors[i % this.config.colors.length];
        this.context.strokeRect(legendsLeft, this.height - categoriesHeight, 30, categoriesHeight - 5);
        legendsLeft += 40;
        
        // Ecriture du nom de la catégorie
        this.context.textAlign = "left";
        this.context.fillStyle = "black";
        this.context.fillText(category, legendsLeft, this.height - 8);
        legendsLeft += this.bitmap.measureTextWidth(category) + 20;
    }
    
    // Réécriture du dessin
    this.bitmap._setDirty();
};

//-----------------------------------------------------------------------------
// Line chart
SphinxChart2.prototype.drawLineFrame = function() {
    // Récupération de la hauteur du titre
    titleHeight = 0;
    if(this.config.title.length > 0) {
        titleHeight = this.bitmap.measureTextHeight(this.config.title);
    }
    
    // Taille de la police des axes
    this.bitmap.fontSize = 18;
    
    // Récupération de la police
    this.context.font = this.bitmap._makeFontNameText();
    
    // Récupération des bornes des données
    datasMin = null;
    if(this.config.options.forceMinToZero) datasMin = 0;
    datasMax = null;
    datasCountMax = 0;
    for(datasCategory of this.config.datas) {
        if(datasMin == null) datasMin = Math.min(...datasCategory);
        else datasMin = Math.min(datasMin, ...datasCategory);
        if(datasMax == null) datasMax = Math.max(...datasCategory);
        else datasMax = Math.max(datasMax, ...datasCategory);
        datasCountMax = Math.max(datasCountMax, datasCategory.length);
    }
    yAxisLimitMin = Math.floor(datasMin / 5) * 5;
    yAxisLimitMax = Math.ceil(datasMax / 5) * 5;
    
    // Calcul de la hauteur des étiquettes de l'axe des abscisses
    xAxisHeight = 0;
    for(label of this.config.xAxisLabels) {
        height = this.bitmap.measureTextHeight(label);
        xAxisHeight = Math.max(xAxisHeight, height);
    }
    
    // Calcul de la hauteur des catégories
    categoriesHeight = 0;
    for(category of this.config.categoriesLabels) {
        height = this.bitmap.measureTextHeight(category);
        categoriesHeight = Math.max(categoriesHeight, height);
    }
    
    // Coordonnées de l'axe des abscisses
    xAxis = {};
    xAxis.width = this.width - Math.max(this.bitmap.measureTextWidth(yAxisLimitMin), this.bitmap.measureTextWidth(yAxisLimitMax)) - this.config.options.padding.left - this.config.options.padding.right;
    
    // Coordonnées de l'axe des ordonnées
    yAxis = {};
    yAxis.left = this.config.options.padding.left + Math.max(this.bitmap.measureTextWidth(yAxisLimitMin), this.bitmap.measureTextWidth(yAxisLimitMax));
    yAxis.top = titleHeight + this.config.options.padding.top + 10;
    yAxis.height = this.height - titleHeight - xAxisHeight - categoriesHeight - this.config.options.padding.top - this.config.options.padding.bottom;
    
    // Dessin des axes des abscisses et des ordonnées
    this.context.beginPath();
    this.context.moveTo(yAxis.left, yAxis.top);
    this.context.lineTo(yAxis.left, yAxis.top + yAxis.height);
    this.context.lineTo(yAxis.left + xAxis.width, yAxis.top + yAxis.height);
    this.context.lineWidth = 2;
    this.context.strokeStyle = "#999999";
    this.context.stroke();
    
    // Ajout de labels manquants sur l'axe des abscisses
    if(this.config.xAxisLabels.length < datasCountMax) {
        for(i = this.config.xAxisLabels.length + 1; i <= datasCountMax; ++i) {
            this.config.xAxisLabels.push(i);
        }
    }
    
    // Ajout de labels manquants sur l'axe des ordonnées
    if(this.config.categoriesLabels.length < this.config.datas.length) {
        for(i = this.config.categoriesLabels.length + 1; i <= this.config.datas.length; ++i) {
            this.config.categoriesLabels.push("Category " + i);
        }
    } else if(this.config.categoriesLabels.length > this.config.datas.length) {
        this.config.categoriesLabels = this.config.categoriesLabels.slice(0, this.config.datas.length);
    }
    
    // Ecriture des étiquettes des abscisses
    xAxisGapWidth = xAxis.width / (datasCountMax - 1);
    for(i = 0; i < datasCountMax; ++i) {
        // Repère abscisse
        this.context.beginPath();
        this.context.moveTo(yAxis.left + xAxisGapWidth * i, yAxis.top + yAxis.height - 10);
        this.context.lineTo(yAxis.left + xAxisGapWidth * i, yAxis.top + yAxis.height + 10);
        this.context.lineWidth = 2;
        this.context.strokeStyle = "#999999";
        this.context.stroke();
        
        // Prolongement repère abscisse
        if(i > 0) {
            this.context.beginPath();
            this.context.moveTo(yAxis.left + xAxisGapWidth * i, yAxis.top + yAxis.height - 10);
            this.context.lineTo(yAxis.left + xAxisGapWidth * i, yAxis.top);
            this.context.lineWidth = 2;
            this.context.strokeStyle = "#CCCCCC";
            this.context.stroke();
        }
        
        // Etiquette abscisse
        this.context.textAlign = "center";
        this.context.fillStyle = "black";
        labelHeight = this.bitmap.measureTextHeight(this.config.xAxisLabels[i]);
        this.context.fillText(this.config.xAxisLabels[i], yAxis.left + xAxisGapWidth * i, yAxis.top + yAxis.height + labelHeight + 5, xAxisGapWidth);
    }

    // Ecriture des étiquettes des ordonnées
    i = 5;
    yAxisTotalGap = Math.ceil((yAxisLimitMax - yAxisLimitMin) / 5) * 5;
    yAxisGap = Math.ceil(yAxisTotalGap / i / 5) * 5;
    yAxisLimitMax = yAxisLimitMin + (yAxisGap * 5);
    yAxisGapHeight = yAxis.height / (yAxisGap * 5 / yAxisGap);
    for(i = 5; i >= 0; --i) {
        // Label abscisse
        yAxisLabel = yAxisLimitMax - yAxisGap * i;
        
        // Repère ordonnée
        this.context.beginPath();
        this.context.moveTo(yAxis.left - 10, yAxis.top + yAxisGapHeight * i);
        this.context.lineTo(yAxis.left + 10, yAxis.top + yAxisGapHeight * i);
        this.context.lineWidth = 2;
        this.context.strokeStyle = "#999999";
        this.context.stroke();
        
        // Prolongement repère ordonnée
        if(i < 5) {
            this.context.beginPath();
            this.context.moveTo(yAxis.left + 10, yAxis.top + yAxisGapHeight * i);
            this.context.lineTo(yAxis.left + xAxis.width, yAxis.top + yAxisGapHeight * i);
            this.context.lineWidth = 2;
            this.context.strokeStyle = "#CCCCCC";
            this.context.stroke();
        }
        
        // Etiquette ordonnée
        this.context.textAlign = "right";
        this.context.fillStyle = "black";
        labelHeight = this.bitmap.measureTextHeight(yAxisLabel);
        this.context.fillText(yAxisLabel, yAxis.left - 15, yAxis.top + yAxisGapHeight * i + labelHeight / 4);
    }
    
    // Tracé des données du graphique
    easing = Math.easing[this.config.options.easing];
    for(i = 0; i < this.config.datas.length; ++i) {
        // Données de la catégorie
        datasCategory = this.config.datas[i];
        
        // Graphique
        this.context.beginPath();
        for(j = 0; j < datasCategory.length; ++j) {
            data = datasCategory[j];
            y = easing(this.frame, yAxis.top + yAxis.height, -yAxisGapHeight * (data - yAxisLimitMin) / yAxisGap, this.config.options.duration * 60);
            if(j == 0) {
                this.context.moveTo(yAxis.left, y);
            } else {
                this.context.lineTo(yAxis.left + xAxisGapWidth * j, y);
            }
        }
        this.context.lineWidth = 2;
        this.context.strokeStyle = this.config.colors[i % this.config.colors.length];
        this.context.stroke();
    }
};

//-----------------------------------------------------------------------------
// Bar chart
SphinxChart2.prototype.drawBarFrame = function() {
    // Récupération de la hauteur du titre
    titleHeight = 0;
    if(this.config.title.length > 0) {
        titleHeight = this.bitmap.measureTextHeight(this.config.title);
    }
    
    // Taille de la police des axes
    this.bitmap.fontSize = 18;
    
    // Récupération de la police
    this.context.font = this.bitmap._makeFontNameText();
    
    // Récupération des bornes des données
    datasMin = null;
    if(this.config.options.forceMinToZero) datasMin = 0;
    datasMax = null;
    datasCountMax = 0;
    for(datasCategory of this.config.datas) {
        if(datasMin == null) datasMin = Math.min(...datasCategory);
        else datasMin = Math.min(datasMin, ...datasCategory);
        if(datasMax == null) datasMax = Math.max(...datasCategory);
        else datasMax = Math.max(datasMax, ...datasCategory);
        datasCountMax = Math.max(datasCountMax, datasCategory.length);
    }
    yAxisLimitMin = Math.floor(datasMin / 5) * 5;
    yAxisLimitMax = Math.ceil(datasMax / 5) * 5;
    
    // Calcul de la hauteur des étiquettes de l'axe des abscisses
    xAxisHeight = 0;
    for(label of this.config.xAxisLabels) {
        height = this.bitmap.measureTextHeight(label);
        xAxisHeight = Math.max(xAxisHeight, height);
    }
    
    // Calcul de la hauteur des catégories
    categoriesHeight = 0;
    for(category of this.config.categoriesLabels) {
        height = this.bitmap.measureTextHeight(category);
        categoriesHeight = Math.max(categoriesHeight, height);
    }
    
    // Ajout de labels manquants sur l'axe des abscisses
    if(this.config.xAxisLabels.length < datasCountMax) {
        for(i = this.config.xAxisLabels.length + 1; i <= datasCountMax; ++i) {
            this.config.xAxisLabels.push(i);
        }
    }
    
    // Ajout de labels manquants sur l'axe des ordonnées
    if(this.config.categoriesLabels.length < this.config.datas.length) {
        for(i = this.config.categoriesLabels.length + 1; i <= this.config.datas.length; ++i) {
            this.config.categoriesLabels.push("Category " + i);
        }
    } else if(this.config.categoriesLabels.length > this.config.datas.length) {
        this.config.categoriesLabels = this.config.categoriesLabels.slice(0, this.config.datas.length);
    }
    
    // Coordonnées de l'axe des abscisses
    xAxis = {};
    xAxis.width = this.width - Math.max(this.bitmap.measureTextWidth(yAxisLimitMin), this.bitmap.measureTextWidth(yAxisLimitMax)) - this.config.options.padding.left - this.config.options.padding.right;
    
    // Coordonnées de l'axe des ordonnées
    yAxis = {};
    yAxis.left = this.config.options.padding.left + Math.max(this.bitmap.measureTextWidth(yAxisLimitMin), this.bitmap.measureTextWidth(yAxisLimitMax));
    yAxis.top = titleHeight + this.config.options.padding.top + 10;
    yAxis.height = this.height - titleHeight - xAxisHeight - categoriesHeight - this.config.options.padding.top - this.config.options.padding.bottom;
    
    // Dessin des axes des abscisses et des ordonnées
    this.context.beginPath();
    this.context.moveTo(yAxis.left, yAxis.top);
    this.context.lineTo(yAxis.left, yAxis.top + yAxis.height);
    this.context.lineTo(yAxis.left + xAxis.width, yAxis.top + yAxis.height);
    this.context.lineWidth = 2;
    this.context.strokeStyle = "#999999";
    this.context.stroke();
    
    // Ecriture des étiquettes des abscisses
    xAxisGapWidth = xAxis.width / datasCountMax;
    this.context.beginPath();
    this.context.moveTo(yAxis.left, yAxis.top + yAxis.height - 10);
    this.context.lineTo(yAxis.left, yAxis.top + yAxis.height + 10);
    this.context.lineWidth = 2;
    this.context.strokeStyle = "#999999";
    this.context.stroke();
    for(i = 0; i < datasCountMax; ++i) {
        // Repère abscisse
        this.context.beginPath();
        this.context.moveTo(yAxis.left + xAxisGapWidth * (i + 1), yAxis.top + yAxis.height - 10);
        this.context.lineTo(yAxis.left + xAxisGapWidth * (i + 1), yAxis.top + yAxis.height + 10);
        this.context.lineWidth = 2;
        this.context.strokeStyle = "#999999";
        this.context.stroke();
        
        // Prolongement repère abscisse
        this.context.beginPath();
        this.context.moveTo(yAxis.left + xAxisGapWidth * (i + 1), yAxis.top + yAxis.height - 10);
        this.context.lineTo(yAxis.left + xAxisGapWidth * (i + 1), yAxis.top);
        this.context.lineWidth = 2;
        this.context.strokeStyle = "#CCCCCC";
        this.context.stroke();
        
        // Etiquette abscisse
        this.context.textAlign = "center";
        this.context.fillStyle = "black";
        labelHeight = this.bitmap.measureTextHeight(this.config.xAxisLabels[i]);
        this.context.fillText(this.config.xAxisLabels[i], yAxis.left + xAxisGapWidth * i + xAxisGapWidth / 2, yAxis.top + yAxis.height + labelHeight + 5, xAxisGapWidth);
    }

    // Ecriture des étiquettes des ordonnées
    i = 5;
    yAxisTotalGap = Math.ceil((yAxisLimitMax - yAxisLimitMin) / 5) * 5;
    yAxisGap = Math.ceil(yAxisTotalGap / i / 5) * 5;
    yAxisLimitMax = yAxisLimitMin + (yAxisGap * 5);
    yAxisGapHeight = yAxis.height / (yAxisGap * 5 / yAxisGap);
    for(i = 5; i >= 0; --i) {
        // Label abscisse
        yAxisLabel = yAxisLimitMax - yAxisGap * i;
        
        // Repère ordonnée
        this.context.beginPath();
        this.context.moveTo(yAxis.left - 10, yAxis.top + yAxisGapHeight * i);
        this.context.lineTo(yAxis.left + 10, yAxis.top + yAxisGapHeight * i);
        this.context.lineWidth = 2;
        this.context.strokeStyle = "#999999";
        this.context.stroke();
        
        // Prolongement repère ordonnée
        if(i < 5) {
            this.context.beginPath();
            this.context.moveTo(yAxis.left + 10, yAxis.top + yAxisGapHeight * i);
            this.context.lineTo(yAxis.left + xAxis.width, yAxis.top + yAxisGapHeight * i);
            this.context.lineWidth = 2;
            this.context.strokeStyle = "#CCCCCC";
            this.context.stroke();
        }
        
        // Etiquette ordonnée
        this.context.textAlign = "right";
        this.context.fillStyle = "black";
        labelHeight = this.bitmap.measureTextHeight(yAxisLabel);
        this.context.fillText(yAxisLabel, yAxis.left - 15, yAxis.top + yAxisGapHeight * i + labelHeight / 4);
    }
    
    // Tracé des données du graphique
    easing = Math.easing[this.config.options.easing];
    for(i = 0; i < this.config.datas.length; ++i) {
        // Données de la catégorie
        datasCategory = this.config.datas[i];
        
        // Graphique
        widthWithSpace = xAxisGapWidth / this.config.datas.length;
        widthWithoutSpace = xAxisGapWidth / (this.config.datas.length + 1);
        widthSpace = widthWithSpace - widthWithoutSpace;
        for(j = 0; j < datasCategory.length; ++j) {
            data = datasCategory[j];
            width = widthWithoutSpace;
            height = easing(this.frame, 0, (data - yAxisLimitMin) * yAxisGapHeight / yAxisGap, this.config.options.duration * 60);
            x = yAxis.left + xAxisGapWidth * j + widthWithSpace * i + widthSpace / 2;
            y = yAxis.top + yAxis.height - height;
            this.context.lineWidth = 2;
            this.context.strokeStyle = this.config.colors[i % this.config.colors.length];
            this.context.strokeRect(x, y, width, height);
            this.context.save();
            this.context.globalAlpha = 0.59765625;
            this.context.fillStyle = this.config.colors[i % this.config.colors.length];
            this.context.fillRect(x, y, width, height);
            this.context.restore();
        }
    }
};

//-----------------------------------------------------------------------------
// Radar chart
SphinxChart2.prototype.drawRadarFrame = function() {
    // Récupération de la hauteur du titre
    titleHeight = 0;
    if(this.config.title.length > 0) {
        titleHeight = this.bitmap.measureTextHeight(this.config.title);
    }
    
    // Taille de la police des axes
    this.bitmap.fontSize = 18;
    
    // Récupération de la police
    this.context.font = this.bitmap._makeFontNameText();
    
    // Récupération des bornes des données
    datasMin = null;
    if(this.config.options.forceMinToZero) datasMin = 0;
    datasMax = null;
    datasCountMax = 0;
    for(datasCategory of this.config.datas) {
        if(datasMin == null) datasMin = Math.min(...datasCategory);
        else datasMin = Math.min(datasMin, ...datasCategory);
        if(datasMax == null) datasMax = Math.max(...datasCategory);
        else datasMax = Math.max(datasMax, ...datasCategory);
        datasCountMax = Math.max(datasCountMax, datasCategory.length);
    }
    yAxisLimitMin = Math.floor(datasMin / 5) * 5;
    yAxisLimitMax = Math.ceil(datasMax / 5) * 5;
    
    // Calcul de la hauteur des étiquettes de l'axe des abscisses
    xAxisHeight = 0;
    for(label of this.config.xAxisLabels) {
        height = this.bitmap.measureTextHeight(label);
        xAxisHeight = Math.max(xAxisHeight, height);
    }
    
    // Calcul de la hauteur des catégories
    categoriesHeight = 0;
    for(category of this.config.categoriesLabels) {
        height = this.bitmap.measureTextHeight(category);
        categoriesHeight = Math.max(categoriesHeight, height);
    }
    
    // Ajout de labels manquants sur l'axe des abscisses
    if(this.config.xAxisLabels.length < datasCountMax) {
        for(i = this.config.xAxisLabels.length + 1; i <= datasCountMax; ++i) {
            this.config.xAxisLabels.push(i);
        }
    }
    
    // Ajout de labels manquants sur l'axe des ordonnées
    if(this.config.categoriesLabels.length < this.config.datas.length) {
        for(i = this.config.categoriesLabels.length + 1; i <= this.config.datas.length; ++i) {
            this.config.categoriesLabels.push("Category " + i);
        }
    } else if(this.config.categoriesLabels.length > this.config.datas.length) {
        this.config.categoriesLabels = this.config.categoriesLabels.slice(0, this.config.datas.length);
    }
    
    // Angle, rayon et centre du radar
    degreesAngle = 360 / datasCountMax;
    ray = Math.min(this.width - 40, this.height - titleHeight - categoriesHeight - 40) / 2;
    centerX = this.width / 2;
    centerY = titleHeight + ray + 20;
    
    // Dessin des rayons
    for(i = 0; i < datasCountMax; ++i) {
        this.context.beginPath();
        this.context.moveTo(centerX, centerY);
        this.context.lineTo(centerX + ray * Math.cos(((270 + degreesAngle * i) % 360) * Math.PI / 180), centerY + ray * Math.sin(((270 + degreesAngle * i) % 360) * Math.PI / 180));
        this.context.lineWidth = 2;
        this.context.strokeStyle = "#999999";
        this.context.stroke();
    }
    
    // Ecriture des étiquettes des abscisses
    for(i = 0; i < this.config.xAxisLabels.length; ++i) {
        label = this.config.xAxisLabels[i];
        this.context.textAlign = "center";
        this.context.fillStyle = "black";
        this.context.fillText(label, centerX + (ray + 10) * Math.cos(((270 + degreesAngle * i) % 360) * Math.PI / 180), centerY + (ray + 10) * Math.sin(((270 + degreesAngle * i) % 360) * Math.PI / 180));
    }
    
    // Ecriture des étiquettes des ordonnées
    i = 5;
    yAxisTotalGap = Math.ceil((yAxisLimitMax - yAxisLimitMin) / 5) * 5;
    yAxisGap = Math.ceil(yAxisTotalGap / i / 5) * 5;
    yAxisLimitMax = yAxisLimitMin + (yAxisGap * 5);
    yAxisGapHeight = ray / (yAxisGap * 5 / yAxisGap);
    for(i = 0; i <= 5; ++i) {
        // Label abscisse
        yAxisLabel = yAxisLimitMin + yAxisGap * i;
        y = centerY - (ray / 5 * i);
        
        // Repère ordonnée
        this.context.beginPath();
        this.context.moveTo(centerX + ray / 5 * i * Math.cos(270 * Math.PI / 180), centerY + ray / 5 * i * Math.sin(270 * Math.PI / 180));
        for(j = 0; j <= datasCountMax; ++j) {
            this.context.lineTo(centerX + ray / 5 * i * Math.cos(((270 + degreesAngle * j) % 360) * Math.PI / 180), centerY + ray / 5 * i * Math.sin(((270 + degreesAngle * j) % 360) * Math.PI / 180));
        }
        this.context.lineWidth = 2;
        this.context.strokeStyle = "#CCCCCC";
        this.context.stroke();
        
        // Etiquette ordonnée
        this.context.textAlign = "right";
        this.context.fillStyle = "black";
        labelHeight = this.bitmap.measureTextHeight(yAxisLabel);
        this.context.fillText(yAxisLabel, centerX - 10, centerY - ray / 5 * i + 10);
    }
    
    // Tracé des données du graphique
    easing = Math.easing[this.config.options.easing];
    for(i = 0; i < this.config.datas.length; ++i) {
        // Données de la catégorie
        datasCategory = this.config.datas[i];
        
        // Graphique
        dataRay = easing(this.frame, 0, ray * (datasCategory[0] - yAxisLimitMin) / (yAxisLimitMax - yAxisLimitMin), this.config.options.duration * 60);
        this.context.beginPath();
        this.context.moveTo(centerX + dataRay * Math.cos(270 * Math.PI / 180), centerY + dataRay * Math.sin(270 * Math.PI / 180));
        for(j = 0; j < datasCategory.length; ++j) {
            data = datasCategory[j];
            dataRay = easing(this.frame, 0, ray * (data - yAxisLimitMin) / (yAxisLimitMax - yAxisLimitMin), this.config.options.duration * 60);
            this.context.lineTo(centerX + dataRay * Math.cos(((270 + degreesAngle * j) % 360) * Math.PI / 180), centerY + dataRay * Math.sin(((270 + degreesAngle * j) % 360) * Math.PI / 180));
        }
        this.context.lineWidth = 2;
        this.context.strokeStyle = this.config.colors[i % this.config.colors.length];
        this.context.stroke();
        this.context.save();
        this.context.globalAlpha = 0.59765625;
        this.context.fillStyle = this.config.colors[i % this.config.colors.length];
        this.context.fill();
        this.context.restore();
    }
};

//-----------------------------------------------------------------------------
// Pie chart
SphinxChart2.prototype.drawPieFrame = function() {
    // Récupération de la hauteur du titre
    titleHeight = 0;
    if(this.config.title.length > 0) {
        titleHeight = this.bitmap.measureTextHeight(this.config.title);
    }
    
    // Taille de la police des axes
    this.bitmap.fontSize = 18;
    
    // Récupération de la police
    this.context.font = this.bitmap._makeFontNameText();
    
    // Calcul de la hauteur des catégories
    categoriesHeight = 0;
    for(category of this.config.categoriesLabels) {
        height = this.bitmap.measureTextHeight(category);
        categoriesHeight = Math.max(categoriesHeight, height);
    }
    
    // Ajout de labels manquants sur l'axe des ordonnées
    if(this.config.categoriesLabels.length < this.config.datas.length) {
        for(i = this.config.categoriesLabels.length + 1; i <= this.config.datas.length; ++i) {
            this.config.categoriesLabels.push("Category " + i);
        }
    } else if(this.config.categoriesLabels.length > this.config.datas.length) {
        this.config.categoriesLabels = this.config.categoriesLabels.slice(0, this.config.datas.length);
    }
    
    // Rayon et centre du camembert
    ray = Math.min(this.width - 40, this.height - titleHeight - categoriesHeight - 40) / 2;
    centerX = this.width / 2;
    centerY = titleHeight + ray + 20;
    
    // Tracé des données du graphique
    easing = Math.easing[this.config.options.easing];
    
    // Calcul du total des données
    sumDatas = 0;
    for(data of this.config.datas) {
        sumDatas += data;
    }
    
    // Dessin du camembert
    lastDegreesAngle = 270;
    for(i = 0; i < this.config.datas.length; ++i) {
        // Données de la catégorie
        data = this.config.datas[i];
        nextDegreesAngle = (lastDegreesAngle + easing(this.frame, 0, 360 * data / sumDatas, this.config.options.duration * 60)) % 360;
        
        // Graphique
        this.context.beginPath();
        this.context.moveTo(centerX, centerY);
        this.context.lineTo(centerX + ray * Math.cos(lastDegreesAngle * Math.PI / 180), centerY + ray * Math.sin(lastDegreesAngle * Math.PI / 180));
        this.context.arc(centerX, centerY, ray, lastDegreesAngle * Math.PI / 180, nextDegreesAngle * Math.PI / 180);
        this.context.lineWidth = 2;
        this.context.strokeStyle = this.config.colors[i % this.config.colors.length];
        this.context.stroke();
        this.context.save();
        this.context.globalAlpha = 0.59765625;
        this.context.fillStyle = this.config.colors[i % this.config.colors.length];
        this.context.fill();
        this.context.restore();
        
        // Décallage
        lastDegreesAngle = nextDegreesAngle;
    }
};

//-----------------------------------------------------------------------------
// Doughnut chart
SphinxChart2.prototype.drawDoughnutFrame = function() {
    // Récupération de la hauteur du titre
    titleHeight = 0;
    if(this.config.title.length > 0) {
        titleHeight = this.bitmap.measureTextHeight(this.config.title);
    }
    
    // Taille de la police des axes
    this.bitmap.fontSize = 18;
    
    // Récupération de la police
    this.context.font = this.bitmap._makeFontNameText();
    
    // Calcul de la hauteur des catégories
    categoriesHeight = 0;
    for(category of this.config.categoriesLabels) {
        height = this.bitmap.measureTextHeight(category);
        categoriesHeight = Math.max(categoriesHeight, height);
    }
    
    // Ajout de labels manquants sur l'axe des ordonnées
    if(this.config.categoriesLabels.length < this.config.datas.length) {
        for(i = this.config.categoriesLabels.length + 1; i <= this.config.datas.length; ++i) {
            this.config.categoriesLabels.push("Category " + i);
        }
    } else if(this.config.categoriesLabels.length > this.config.datas.length) {
        this.config.categoriesLabels = this.config.categoriesLabels.slice(0, this.config.datas.length);
    }
    
    // Rayon et centre du camembert
    ray = Math.min(this.width - 40, this.height - titleHeight - categoriesHeight - 40) / 2;
    centerX = this.width / 2;
    centerY = titleHeight + ray + 20;
    
    // Tracé des données du graphique
    easing = Math.easing[this.config.options.easing];
    
    // Calcul du total des données
    sumDatas = 0;
    for(data of this.config.datas) {
        sumDatas += data;
    }
    
    // Dessin du camembert
    lastDegreesAngle = 270;
    for(i = 0; i < this.config.datas.length; ++i) {
        // Données de la catégorie
        data = this.config.datas[i];
        nextDegreesAngle = (lastDegreesAngle + easing(this.frame, 0, 360 * data / sumDatas, this.config.options.duration * 60)) % 360;
        
        // Graphique
        this.context.beginPath();
        this.context.moveTo(centerX + ray / 2 * Math.cos(lastDegreesAngle * Math.PI / 180), centerY + ray / 2 * Math.sin(lastDegreesAngle * Math.PI / 180));
        this.context.lineTo(centerX + ray * Math.cos(lastDegreesAngle * Math.PI / 180), centerY + ray * Math.sin(lastDegreesAngle * Math.PI / 180));
        this.context.arc(centerX, centerY, ray, lastDegreesAngle * Math.PI / 180, nextDegreesAngle * Math.PI / 180);
        this.context.lineTo(centerX + ray / 2 * Math.cos(nextDegreesAngle * Math.PI / 180), centerY + ray / 2 * Math.sin(nextDegreesAngle * Math.PI / 180));
        this.context.arc(centerX, centerY, ray / 2, nextDegreesAngle * Math.PI / 180, lastDegreesAngle * Math.PI / 180, true);
        this.context.lineWidth = 2;
        this.context.strokeStyle = this.config.colors[i % this.config.colors.length];
        this.context.stroke();
        this.context.save();
        this.context.globalAlpha = 0.59765625;
        this.context.fillStyle = this.config.colors[i % this.config.colors.length];
        this.context.fill();
        this.context.restore();
        
        // Décallage
        lastDegreesAngle = nextDegreesAngle;
    }
};

C’est un sacré bloc. Il contient la définition des 5 formes de graphiques actuellement prises en charge.

Bon d’accord, et comment on s’en sert ?

Actuellement, ce plugin ne pourra être utile qu’à des scripteurs qui souhaiteraient intégrer l’affichage d’un ou de plusieurs graphiques dans leur script. Un exemple d’utilisation pourraient être d’afficher les statistiques du joueur différemment, sous forme d’un graphique (type radar par exemple).

Ca doit être tordu à utiliser sans doute…

Effectivement, pour afficher un graphique, il faut en passer par une étape assez éprouvante : dire au système ce qu’il doit afficher et comment. Toutes les informations à ce sujet sont disponibles dans l’entête du plugin.

Du boulot, du boulot, encore du boulot… Et pour quel résultat d’abord ?

Pour tester ces graphiques, j’ai bricolé une scène dans un plugin annexe. Celui-ci n’est pas requis pour utiliser le système, mais si vous voulez un apercu des possibilités, je vous recommande de l’ajouter et de démarrer la scène :

//=============================================================================
// Sphinx-Chart2Scene.js
//=============================================================================

/*:
 * @plugindesc Scène de démonstration du plugin Sphinx-Chart2
 * @author Sphinx
 *
 * @help
 * //==========================================================================
 * // Plugin : Sphinx-Chart2Scene
 * // Date   : 30 décembre 2019
 * // Auteur : Sphinx
 * //==========================================================================
 * 
 * Dépendances :
 *     - Sphinx-Chart2
 * @param background
 * @text Image d'arrière plan de la scène
 * @type file
 * @dir img/titles1/
 * @require 1
 * 
 * @param enemies
 * @text Images d'arrière plan des graphiques
 * @type file[]
 * @dir img/enemies/
 * @require 1
 */

CHART_TYPES = [ "line", "bar", "radar", "pie", "doughnut" ];
AVAILABLE_EASING = [ "linearTween", "easeInQuad", "easeOutQuad", "easeInOutQuad", "easeInCubic", "easeOutCubic", "easeInOutCubic", "easeInQuart", "easeOutQuart", "easeInOutQuart", "easeInQuint", "easeOutQuint", "easeInOutQuint", "easeInSine", "easeOutSine", "easeInOutSine", "easeInExpo", "easeOutExpo", "easeInOutExpo", "easeInCirc", "easeOutCirc", "easeInOutCirc" ];
SYLLABES_DEBUT = [ "Bi", "Tor", "Teri", "Alim", "Cre", "Dri", "Epi", "Fra", "Gro", "Hima", "Jutu", "Kima", "Lu", "Mi", "No", "Noto", "Ori", "Pru", "Qu", "Ria", "Su", "Umako", "Vime", "Wito", "Xi", "Ya", "Zumo", "Api", "Bele", "Cepi", "Do", "Ea", "Fe", "Gato", "Holo", "Ipe", "Jo", "Kala", "Lara", "Meta", "Noxa", "Orra", "Pi", "Quima", "Ro", "Sele", "Ule", "Vi", "Waxa", "Xo", "Yora", "Zuli" ];
SYLLABES_FIN = [ "von", "oll", "tol", "gon", "prim", "tes", "lup", "vim", "qua", "pip", "am", "lm", "thor", "gig", "ve", "qum", "la", "leuth", "bas", "ro", "porr", "lia", "w", "kra", "cor", "mmon" ];
BACKGROUND_IMAGES = JSON.parse(PluginManager.parameters("Sphinx-Chart2Scene")["enemies"]);
CSS_COLORS = [ "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "DarkGrey", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkSlateGrey", "DarkTurquoise", "DarkViolet", "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Grey", "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed", "Indigo", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "LightGoldenRodYellow", "LightGray", "LightGrey", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSlateGrey", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "RebeccaPurple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "SlateGrey", "Snow", "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen" ];

//-----------------------------------------------------------------------------
// Game_Interpreter
//
// Commandes de plugin
Sphinx_Chart_Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
    Sphinx_Chart_Game_Interpreter_pluginCommand.call(this, command, args);
    if(command === "DEMO_GRAPH") {
        SceneManager.push(Scene_Chart2);
    }
};

//-----------------------------------------------------------------------------
// Scene_Chart2
//
// Scène de démonstration du plugin Sphinx-Chart2
function Scene_Chart2() {
    this.initialize.apply(this, arguments);
};

Scene_Chart2.prototype = Object.create(Scene_Base.prototype);
Scene_Chart2.prototype.constructor = Scene_Chart2;

Scene_Chart2.prototype.initialize = function() {
    Scene_Base.prototype.initialize.call(this);
};

Scene_Chart2.prototype.create = function() {
    Scene_Base.prototype.create.call(this);
    this.createWindowLayer();
    this.createBackground();
    this.createWindows();
    this.createTexts();
};

Scene_Chart2.prototype.start = function() {
    Scene_Base.prototype.start.call(this);
    this.startFadeIn(this.fadeSpeed(), false);
    this._windowTypeChart.open();
};

Scene_Chart2.prototype.terminate = function() {
    Scene_Base.prototype.terminate.call(this);
    SceneManager.goto(Scene_Map);
    this.startFadeOut(this.fadeSpeed(), false);
};

Scene_Chart2.prototype.update = function() {
    Scene_Base.prototype.update.call(this);
    if(Input.isTriggered("cancel")) {
        SoundManager.playCancel();
        this.terminate();
    }
};

Scene_Chart2.prototype.createBackground = function() {
    this._background = new Sprite_Base();
    background = PluginManager.parameters("Sphinx-Chart2Scene")["background"];
    this._background.bitmap = ImageManager.loadTitle1(background);
    this.addChildAt(this._background, 0);
};

Scene_Chart2.prototype.createWindows = function() {
    this._windowTypeChart = new Window_TypeChart();
    for(type of CHART_TYPES) {
        this._windowTypeChart.setHandler("draw_" + type, this.chooseEasing.bind(this, type));
    }
    this.addWindow(this._windowTypeChart);
    
    this._windowChooseEasing = new Window_ChooseEasing();
    for(easing of AVAILABLE_EASING) {
        this._windowChooseEasing.setHandler("choose_" + easing, this.changeGraph.bind(this, easing));
    }
    this.addWindow(this._windowChooseEasing);
};

Scene_Chart2.prototype.createTexts = function() {
    this._titleText = new Sprite_Base();
    this._titleText.x = 0;
    this._titleText.y = 0;
    this._titleText.width = Graphics.boxWidth;
    this._titleText.height = (Graphics.boxHeight - 480) / 2;
    this._titleText.bitmap = new Bitmap(Graphics.boxWidth, (Graphics.boxHeight - 480) / 2);
    this._titleText.bitmap.drawText("Démonstration - Sphinx-Chart2", 0, 16, Graphics.boxWidth - 32, 40, "center");
    this.addChildAt(this._titleText, 2);
    
    this._explicationText1 = new Sprite_Base();
    this._explicationText1.x = 0;
    this._explicationText1.y = Graphics.boxHeight - ((Graphics.boxHeight - 480) / 2);
    this._explicationText1.width = Graphics.boxWidth;
    this._explicationText1.height = (Graphics.boxHeight - 480) / 4;
    this._explicationText1.bitmap = new Bitmap(Graphics.boxWidth, (Graphics.boxHeight - 480) / 4);
    this._explicationText1.bitmap.fontSize = 18;
    this._explicationText1.bitmap.drawText("Choisissez un type de graphique, puis un type d'accélération.", 0, 8, Graphics.boxWidth - 32, 20, "center");
    this.addChildAt(this._explicationText1, 3);
    
    this._explicationText2 = new Sprite_Base();
    this._explicationText2.x = 0;
    this._explicationText2.y = Graphics.boxHeight - ((Graphics.boxHeight - 480) / 4);
    this._explicationText2.width = Graphics.boxWidth;
    this._explicationText2.height = (Graphics.boxHeight - 480) / 4;
    this._explicationText2.bitmap = new Bitmap(Graphics.boxWidth, (Graphics.boxHeight - 480) / 4);
    this._explicationText2.bitmap.fontSize = 18;
    this._explicationText2.bitmap.drawText("Vous pouvez cliquer sur le graphique pour relancer l'animation.", 0, 8, Graphics.boxWidth - 32, 20, "center");
    this.addChildAt(this._explicationText2, 4);
};

Scene_Chart2.prototype.chooseEasing = function(type) {
    if(this.chart) this.removeChild(this.chart);
    this.type = type;
    this._windowChooseEasing.open();
    this._windowChooseEasing.activate();
}

Scene_Chart2.prototype.changeGraph = function(easing) {
    this._windowChooseEasing.close();
    categories = this.getCategories(this.type);
    xLabels = this.getXLabels(this.type);
    datas = this.getDatas(this.type);
    background = this.getRandomChartBackground();
    console.log(this.type, datas);
    this.chart = new SphinxChart2({
        type: this.type,
        title: this.type.capitalize() + " - " + easing.capitalize(),
        categoriesLabels: categories,
        xAxisLabels: xLabels,
        datas: datas,
        options: {
            easing: easing,
            duration: 1,
            background: background,
            forceMinToZero: false,
            position: {
                x: 166,
                y: (Graphics.boxHeight - 480) / 2,
                width: 640,
                height: 480,
            },
            padding: {
                top: 10,
                left: 25,
                right: 25,
                bottom: 25,
            }
        }
    });
    this.addChildAt(this.chart, 1);
    this._windowTypeChart.activate();
};

Scene_Chart2.prototype.getCategories = function(type) {
    switch(type) {
        case "line":
        case "bar":
        case "radar":
        case "pie":
        case "doughnut":
            noms = [];
            for(i = 1; i <= 10; ++i) {
                noms.push(SYLLABES_DEBUT[Math.randomIntBetween(0, SYLLABES_DEBUT.length)] + SYLLABES_FIN[Math.randomIntBetween(0, SYLLABES_FIN.length)]);
            }
            return noms;
    }
};

Scene_Chart2.prototype.getXLabels = function(type) {
    switch(type) {
        case "line":
            return [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
        case "bar":
        case "radar":
            noms = [];
            for(i = 1; i <= 10; ++i) {
                noms.push(SYLLABES_DEBUT[Math.randomIntBetween(0, SYLLABES_DEBUT.length)] + SYLLABES_FIN[Math.randomIntBetween(0, SYLLABES_FIN.length)]);
            }
            return noms;
        case "pie":
        case "doughnut":
            return null;
    }
};

Scene_Chart2.prototype.getDatas = function(type) {
    switch(type) {
        case "line":
        case "bar":
        case "radar":
            datas = [];
            numberDatasInCategory = Math.randomIntBetween(2, 11);
            maxI = Math.randomIntBetween(2, 6);
            for(i = 1; i <= maxI; ++i) {
                datasCategory = [];
                for(j = 1; j <= numberDatasInCategory; ++j) {
                    datasCategory.push(Math.randomIntBetween(5, 51));
                }
                datas.push(datasCategory);
            }
            return datas;
        case "pie":
        case "doughnut":
            datas = [];
            for(i = 1; i <= Math.randomIntBetween(2, 6); ++i) {
                datas.push(Math.randomIntBetween(5, 51));
            }
            return datas;
    }
};

Scene_Chart2.prototype.getRandomChartBackground = function() {
    switch(Math.randomIntBetween(0, 4)) {
        case 0:
            return "transparent";
        case 1:
            if(BACKGROUND_IMAGES.length == 0) return "transparent";
            return ImageManager.loadEnemy(BACKGROUND_IMAGES[Math.randomIntBetween(0, BACKGROUND_IMAGES.length)]);
        case 2:
            colors = [];
            for(i = 1; i <= Math.randomIntBetween(2, 11); ++i) {
                colors.push(CSS_COLORS[Math.randomIntBetween(0, CSS_COLORS.length)]);
            }
            return colors;
        case 3:
            return CSS_COLORS[Math.randomIntBetween(0, CSS_COLORS.length)];
    }
};

Scene_Chart2.prototype.fastForwardRate = function() {
    return 3;
};

Scene_Chart2.prototype.isFastForward = function() {
    return (Input.isPressed('ok') || Input.isPressed('shift') || TouchInput.isPressed());
};

Scene_Chart2.prototype.updateFade = function() {
    if(this._fadeDuration > 0) {
        var d = this._fadeDuration;
        if(this.isFastForward()) {
            d -= this.fastForwardRate() - 1;
            this._fadeDuration -= this.fastForwardRate();
        } else {
            this._fadeDuration--;
        }
        if(this._fadeSign > 0) {
            this._fadeSprite.opacity -= this._fadeSprite.opacity / d;
        } else {
            this._fadeSprite.opacity += (255 - this._fadeSprite.opacity) / d;
        }
    }
};

Scene_Chart2.prototype.fadeSpeed = function(ignoreFast) {
    return 30;
};

//-----------------------------------------------------------------------------
// Window_TypeChart
//
// Fenêtre de sélection du type du graphique

function Window_TypeChart() {
    this.initialize.apply(this, arguments);
};

Window_TypeChart.prototype = Object.create(Window_Command.prototype);
Window_TypeChart.prototype.constructor = Window_TypeChart;

Window_TypeChart.prototype.initialize = function() {
    Window_Command.prototype.initialize.call(this, 0, 0);
    this.updatePlacement();
};

Window_TypeChart.prototype.windowWidth = function() {
    return 150;
};

Window_TypeChart.prototype.updatePlacement = function() {
    this.x = 8;
    this.y = (Graphics.boxHeight - this.height) / 2;
};

Window_TypeChart.prototype.makeCommandList = function() {
    for(type of CHART_TYPES) {
        this.addCommand(type.capitalize(), "draw_" + type);
    }
};

Window_TypeChart.prototype.isContinueEnabled = function() {
    return DataManager.isAnySavefileExists();
};

//-----------------------------------------------------------------------------
// Window_ChooseEasing
//
// Fenêtre de sélection du type d'accélération

function Window_ChooseEasing() {
    this.initialize.apply(this, arguments);
};

Window_ChooseEasing.prototype = Object.create(Window_Command.prototype);
Window_ChooseEasing.prototype.constructor = Window_ChooseEasing;

Window_ChooseEasing.prototype.initialize = function() {
    Window_Command.prototype.initialize.call(this, 0, 0);
    this.close();
    this.updatePlacement();
};

Window_ChooseEasing.prototype.maxCols = function() {
    return 3;
};

Window_ChooseEasing.prototype.windowWidth = function() {
    return Graphics.boxWidth - 64;
};

Window_ChooseEasing.prototype.updatePlacement = function() {
    this.x = (Graphics.boxWidth - this.width) / 2;
    this.y = (Graphics.boxHeight - this.height) / 2;
};

Window_ChooseEasing.prototype.makeCommandList = function() {
    for(easing of AVAILABLE_EASING) {
        this.addCommand(easing.capitalize(), "choose_" + easing);
    }
};

Window_ChooseEasing.prototype.isContinueEnabled = function() {
    return DataManager.isAnySavefileExists();
};

Cette scène se démarre au moyen d’une commande de plugin suivante : DEMO_GRAPH.

Voici quelques exemples de graphiques (réalisés au moyen de cette scène de démonstration) :

Graphique de type Line
Graphique de type Bar
Graphique de type Radar

Outre ces types de graphiques, le système permet de gérer également des graphiques camenbert et donnuts.

Enjoy it !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.