Moteur graphique basique en PHP

Mammouth du PHP | 1966 Messages

19 sept. 2010, 21:56

Bonjour à tous,

J'ai cherché il y a quelque temps à coder un moteur graphique simple en PHP. J'ai abouti à une version utilisable que je me permet de vous livrer ci-dessous.

J'ai dès le début pris quelques hypothèses simplificatrices par rapport à mon utilisation personnelle. Si quelqu'un implante une fonctionnalité en plus, je suis très interessé par le code en question.
L'application est la représentation de puzzle de type rubik's cube, j'ai donc des images avec beaucoup de polygone superposé qu'il est néccéssaire de trié. J'ai en premier lieu tenter une gestion des polygones par découpage en pixels et gestion d'un z-index, mais les temps d'éxécution était extrément plus long. J'ai finalement opté pour une solution de tri de mes polygones selon l'algorythme BSP
Je n'ai pas gerés d'ombre, de lumière ou de perspective. J'ai considérés qu'un sous groupe de polygones (ceux formant les face intérieures d'un rubik's cube par exemple) était toujours en dessous des polygones de premier plan. Je les distingue par leur couleur grise et les traite en premier. Cela réduit mon arbre BSP en deux arbres dont la taille est réduite par deux.
Les couleurs sont géré par un tableau reprenant les composantes RVB de chaque couleurs, et les reliant un numéro par la définition d'une constante (les 3 premiers numéros (0, 1 et 2) sont reservés aux couleurs prédéfinies (gris claire, noir, gris). Le gris claire sert de couleur de fond, le noir à tracer le bords des polygones et le gris pour les polygones de seconde zone. Un tableau classique de couleurs :
$couleurs = array(    'vert' => array('R' => 0,'V' => 182,'B' => 72),
                            'bleu' => array('R' => 0,'V' => 0,'B' => 255),
                            'blanc' => array('R' => 255,'V' => 255,'B' => 255),
                            'jaune' => array('R' => 255,'V' => 255,'B' => 0),
                            'orange' => array('R' => 255,'V' => 80,'B' => 0),
                            'rouge' => array('R' => 255,'V' => 0,'B' => 0));
define('vert',3);define('bleu',4);define('blanc',5);define('jaune',6);define('orange',7);define('rouge',8);
C'est un peu compliqué, mais cela provient de la gestion globale des couleurs pour mon application.

Le code est orienté objet, pour créer une image, il faut creer un objet appelé squelettes en lui indiquant le tableaux de couleurs. La taille de l'image est geré par une constante, mais il est facile de l'ajouter comme paramètres.
Ensuite, les polygones sont ajouter grâce à la méthode ajout_polygone. Elle prend en argument, la couleur (le nombre défini comme constante ou 2 pour les polygones gris) et le tableau des points du polygones.
2 méthodes déclanchent le classement et le dessin des polygones et l'enregistrement de l'image. $squelette->dessine_image(); et $squelette->termine_image($nom); ($nom est le nom de l'image, le dossier est geré par la constante IMAGE, mais peutt aussi facilement être ajouté comme paramètre)
$squelette = new squelette($couleurs);
$points = array()
$points[] = array('x' => 0, 'y' => 0, 'z' => 0);
//Ajout des points des sommets du polygones
$squelette->ajout_polygone($couleur,$points);
//Autre ajout de polygone
$squelette->dessine_image();
$squelette->termine_image($nom);
Ce projet est utilisable, mais plutot pour quelqu'un connaissant le php et pouvant le comprendre pour l'adapter. Si j'était tomber sur ceci lors de ma recherche préliminaire, j'aurais été enchanté. Pour comprendre comment sont classés les polygones, une recherche sur les mots-clés "arbre BSP" vous donnera laréponse.

Je suis ouvert à toutes critiques.

Enfin voici le code du moteur en question
<?php
define('SIZE',300);
define('IMAGE','');//Défini le dossier d'enregistrement des images
class squelette {
    public $points = array();
    public $polygones;
    public $id = -1;
    public $cpt1 = 0;
    public $cpt2 = 0;
    public $cpt3 = 0;
    public $cpt4 = 0;
    public function __construct($array_couleurs)
    {
        $this->z_buffer = array_fill(0,SIZE,array_fill(0,SIZE,(float)0));
        $this->im = imagecreate(SIZE,SIZE);
        imageantialias ( $this->im, true );//A commentez si non geré par votre configuration PHP
        // allocate colors
        $back = imagecolorallocate( $this->im, 238, 238, 238 );    //0
        $col_poly = imagecolorallocate( $this->im, 0, 0, 0 );        //1
        $grey = imagecolorallocate( $this->im, 200, 200, 200 );    //2, définie comme arrière plan
        foreach($array_couleurs as $cle => $array)
        {
            $$cle = imagecolorallocate( $this->im, $array['R'], $array['V'], $array['B']);
        }
    }
    public function ajout_polygone($couleur,$points)
    {
        $prod_vect =    ($points[0]['x']-$points[1]['x'])
                            *($points[2]['y']-$points[1]['y'])
                            -($points[0]['y']-$points[1]['y'])
                            *($points[2]['x']-$points[1]['x']);
        if($prod_vect < 0)
        {
            $temp = new polygon($points,$couleur,array_fill(0,sizeof($points),1));
            $this->plan($points,$temp);
            if($couleur != 2)
            {
                $this->polygones[] = $temp;
            }
            else
            {
                $this->polygones_gris[] = $temp;
            }
        }
    }
    public function dessine_image()
    {
        if (isset($this->polygones_gris))
        {
            $arbre_bsp = $this->classement_polygones($this->polygones_gris);
            $this->dessine_arbre($arbre_bsp,array('x' => SIZE / 2,'y' => SIZE / 2, 'z' => 10000));
        }
        if (isset($this->polygones))
        {
            $arbre_bsp = $this->classement_polygones($this->polygones);
            $this->dessine_arbre($arbre_bsp,array('x' => SIZE / 2,'y' => SIZE / 2, 'z' => 10000));
        }
        //Les valeurs de 10000 reprise deux fois ci-dessus sont arbitraire et dépende de la taille de l'image, mais ne devrait influencer que le temps d'éxécution
    }
    public function dessine_arbre($arbre_bsp,$point_oeil)
    {
        $premier_polygone = $arbre_bsp[0][0];
        $plan_a = $premier_polygone->a;
        $plan_b = $premier_polygone->b;
        $plan_c = $premier_polygone->c;
        $plan_d = $premier_polygone->d;
        $val_pt = zero(    $plan_a * $point_oeil['x'] +
                                $plan_b * $point_oeil['y'] +
                                $plan_c * $point_oeil['z'] +
                                $plan_d);
        if ($val_pt > 0)
        {
            if (sizeof($arbre_bsp[2]) > 0) $this->dessine_arbre($arbre_bsp[2],$point_oeil);
            $this->dessine_polygones($arbre_bsp[0]);
            if (sizeof($arbre_bsp[1]) > 0) $this->dessine_arbre($arbre_bsp[1],$point_oeil);
        }
        elseif ($val_pt < 0)
        {
            if (sizeof($arbre_bsp[1]) > 0) $this->dessine_arbre($arbre_bsp[1],$point_oeil);
            $this->dessine_polygones($arbre_bsp[0]);
            if (sizeof($arbre_bsp[2]) > 0) $this->dessine_arbre($arbre_bsp[2],$point_oeil);
        }
        elseif ($val_pt == 0)
        {
            if (sizeof($arbre_bsp[1]) > 0) $this->dessine_arbre($arbre_bsp[1],$point_oeil);
            if (sizeof($arbre_bsp[2]) > 0) $this->dessine_arbre($arbre_bsp[2],$point_oeil);
        }
        return;
    }
    public function dessine_polygones ($polygones)
    {
        foreach ($polygones as $polygone)
        {
            $points = array();
            foreach ($polygone->points as $point)
            {
                $points[] = $point['x'];
                $points[] = $point['y'];
            }
            imagefilledpolygon($this->im,$points,$polygone->n_points,$polygone->couleur);
            //Début de la gestion du dessin des bords des polygones
            $j = $polygone->n_points - 1;
            $i = 0;
            foreach($polygone->cote as $cote)
            {
                if ($cote == 1)
                {
                    imageline($this->im,$polygone->points[$j]['x'],$polygone->points[$j]['y'],$polygone->points[$i]['x'],$polygone->points[$i]['y'],1);
                }
                $j = $i;
                $i++;
                if ($i == $polygone->n_points) $i = 0;
            }
        }
        return;
    }
    public function termine_image ($nom)
    {
        //imagecolortransparent( $this->im, $back );//si on veut un fond transparent, décommentez cette ligne
        // Envoie de l'image au client
        imagegif($this->im,"IMAGE/$nom.gif");
        imagedestroy($this->im);
        chmod("IMAGE/$nom.gif", 0666);//Permet la gestion de l'image via ftp
    }
    public function plan($points,$polygone)
    {
        $a =     (($points[1]['y']-$points[0]['y'])
                *($points[2]['z']-$points[0]['z']))
                -(($points[1]['z']-$points[0]['z'])
                *($points[2]['y']-$points[0]['y']));
        $b =     (($points[1]['z']-$points[0]['z'])
                *($points[2]['x']-$points[0]['x']))
                -(($points[1]['x']-$points[0]['x'])
                *($points[2]['z']-$points[0]['z']));
        $c =     (($points[1]['x']-$points[0]['x'])
                *($points[2]['y']-$points[0]['y']))
                -(($points[1]['y']-$points[0]['y'])
                *($points[2]['x']-$points[0]['x']));
        $d = -($a*$points[0]['x']+$b*$points[0]['y']+$c*$points[0]['z']);
        $polygone->a = ($a);
        $polygone->b = ($b);
        $polygone->c = ($c);
        $polygone->d = $d;
        //equation du type ax + by + cz + d = 0
    }
    public function classement_polygones ($polygones,$arbre_bsp = array())
    {
        $premier_polygone = array_pop($polygones);
        $plan_a = $premier_polygone->a;
        $plan_b = $premier_polygone->b;
        $plan_c = $premier_polygone->c;
        $plan_d = $premier_polygone->d;
        $arbre_bsp = array(array($premier_polygone),array(),array());
        $polygones_up = array();
        $polygones_down = array();
        foreach($polygones as $polygone)
        {
            $val_pt = array();
            $i = 0;
            while (isset($polygone->points[$i]))
            {
                $val_pt[$i] = zero(    $plan_a * $polygone->points[$i]['x'] +
                                            $plan_b * $polygone->points[$i]['y'] +
                                            $plan_c * $polygone->points[$i]['z'] +
                                            $plan_d);
                $i++;
            }
            if(!isset($val_pt[3])) $val_pt[3] = 0;
            if ($val_pt[0] == 0 && $val_pt[1] == 0 && $val_pt[2] == 0 && $val_pt[3] == 0)
            {
                $arbre_bsp[0][] = $polygone;
            }
            elseif ($val_pt[0] >= 0 && $val_pt[1] >= 0 && $val_pt[2] >= 0 && $val_pt[3] >= 0)
            {
                $polygones_up[] = $polygone;
            }
            elseif ($val_pt[0] <= 0 && $val_pt[1] <= 0 && $val_pt[2] <= 0 && $val_pt[3] <= 0)
            {
                $polygones_down[] = $polygone;
            }
            else
            {
                list($polygone_up,$polygone_down) = $this->split_polygone($polygone,$val_pt,array($plan_a,$plan_b,$plan_c,$plan_d));
                if (!is_null($polygone_down)) $polygones_down[] = $polygone_down;
                if (!is_null($polygone_up)) $polygones_up[] = $polygone_up;
            }
        }
        if (!empty($polygones_up))
        {
            $arbre_bsp[1] = $this->classement_polygones($polygones_up);
        }
        if (!empty($polygones_down))
        {
            $arbre_bsp[2] = $this->classement_polygones($polygones_down);
        }
        return $arbre_bsp;
    }
    public function split_polygone ($polygone,$val_pt,$plan)
    {
        $n = $polygone->n_points;
        $point_a = $polygone->points[$n - 1];
        $val_pt_a = $val_pt[$n - 1];
        $points_sup = array();
        $points_inf = array();
        $cote_sup = array();
        $cote_inf = array();
        for($i = 0;$i < $n;$i++)
        {
            $point_b = $polygone->points[$i];
            $val_pt_b = $val_pt[$i];
            $val_cote_b = $polygone->cote[$i];
            if ($val_pt_b < 0)
            {
                if ($val_pt_a > 0)
                {
                    $points_sup[] = $points_inf[] = intersection_plan_points($point_a,$point_b,$plan);
                    $cote_inf[] = 0;
                    $cote_sup[] = $val_cote_b;
                }
                $points_inf[] = $point_b;
                $cote_inf[] = $val_cote_b;
            }
            elseif ($val_pt_b > 0)
            {
                if ($val_pt_a < 0)
                {
                    $points_sup[] = $points_inf[] = intersection_plan_points($point_a,$point_b,$plan);
                    $cote_inf[] = $val_cote_b;
                    $cote_sup[] = 0;
                }
                $points_sup[] = $point_b;
                $cote_sup[] = $val_cote_b;
            }
            else
            {
                $points_inf[] = $points_sup[] = $point_b;
                if ($val_pt_a < 0)
                {
                    $cote_inf[] = $val_cote_b;
                    $cote_sup[] = 0;
                }
                elseif ($val_pt_a > 0)
                {
                    $cote_inf[] = 0;
                    $cote_sup[] = $val_cote_b;
                }
                else
                {
                    debug('je crois que ce cas doit pas exister');
                    //Contactez moi avec votre cas si vous obtenez ce message (un var_dump du polygone en question suffirai)
                }
            }
            $point_a = $point_b;
            $val_pt_a = $val_pt_b;
        }
        if(sizeof($points_sup) > 0)
        {
            
            $polygone_sup = new polygon($points_sup,$polygone->couleur,$cote_sup);
            $polygone_sup->a = $polygone->a;
            $polygone_sup->b = $polygone->b;
            $polygone_sup->c = $polygone->c;
            $polygone_sup->d = $polygone->d;
        }
        if(sizeof($points_inf) > 0)
        {
            $polygone_inf = new polygon($points_inf,$polygone->couleur,$cote_inf);
            $polygone_inf->a = $polygone->a;
            $polygone_inf->b = $polygone->b;
            $polygone_inf->c = $polygone->c;
            $polygone_inf->d = $polygone->d;
        }
        return array ($polygone_sup,$polygone_inf);
    }
}
class polygon {
    public $points;
    public $n_points;
    public $couleur;
    public $cote;
    public $a;
    public $b;
    public $c;
    public $d;
    public function __construct($points,$couleur,$cote)
    {
        $this->points = $points;
        $this->n_points = sizeof($points);
        $this->couleur = $couleur;
        $this->cote = $cote;
    }
}
function intersection_plan_points($point1,$point2,$plan)
{
    list($a,$b,$c,$d) = $plan;
    $t =    ($d + $a * $point1['x'] + $b * $point1['y'] + $c * $point1['z']) / 
            ($a * ($point1['x'] - $point2['x']) + $b * ($point1['y'] - $point2['y']) + $c * ($point1['z'] - $point2['z']));
    return array('x' => $point1['x'] + ($point2['x'] - $point1['x']) * $t, 'y' => $point1['y'] + ($point2['y'] - $point1['y']) * $t, 'z' => $point1['z'] + ($point2['z'] - $point1['z']) * $t);
}
function zero($val)
{
    if ($val < pow(10,-5) && $val > -pow(10,-5))
    return 0;
    else return $val;
    //Les valeurs de 10E5 et 10E-5 sont prise arbitrairement, et peuvent dépendre de votre application
}
?>
Spols
pour les fan de rubik's cube ou pour les curieux ==> le portail francophone du rubik's cube

ViPHP
ViPHP | 5462 Messages

19 sept. 2010, 22:34

avec l'exemple je me retrouve avec ca
Notice: Undefined offset: 1 in /Library/WebServer/Documents/test/index.php on line 28
Notice: Undefined offset: 2 in /Library/WebServer/Documents/test/index.php on line 29
Notice: Undefined offset: 1 in /Library/WebServer/Documents/test/index.php on line 29
Notice: Undefined offset: 1 in /Library/WebServer/Documents/test/index.php on line 30
Notice: Undefined offset: 2 in /Library/WebServer/Documents/test/index.php on line 31
Notice: Undefined offset: 1 in /Library/WebServer/Documents/test/index.php on line 31

Mammouth du PHP | 1966 Messages

19 sept. 2010, 22:41

Avec un polygone en un point c'est logique, difficile de le dessiner
Spols
pour les fan de rubik's cube ou pour les curieux ==> le portail francophone du rubik's cube

ViPHP
ViPHP | 5462 Messages

19 sept. 2010, 22:47

Avec un polygone en un point c'est logique, difficile de le dessiner
c'est que t'as mis en exemple, t'en a pas un qui marche ?

Mammouth du PHP | 1966 Messages

19 sept. 2010, 22:52

non pas comme ca, seulement implanté dans une application complète

http://spols.ovh.org/square1V1.1.php

Ce script va plus loin, il gère des gif animé mais ca prends quelques secondes à générer
exemple final
Image

Si j'ai le temps, je ferais un petit script test fonctionnel, mais je sais pas faire cela de tête
Spols
pour les fan de rubik's cube ou pour les curieux ==> le portail francophone du rubik's cube