[php5] phpdraw - le photoshop de php (ou presque...)

Description

Hello,

bon, ce code ne sert pas à grand chose, c'est un amusement. Etant donné que je ne parviens pas à uploader la 3ème partie de mon tuto sur les design patterns, je me suis amusé à approfondir le dernier exemple de ce tuto sur le design pattern INTERPRETER.
Ce code est une petite interface en Ajax permettant de générer une image grâce à des commandes simples : right 50 tracera un trait de 50 pixels vers la droite. On peut les enchaîner:
right 50 down 20 right 60 up 50
Les possibilités sont :
right, left, up, down suivis chacun d'un entier en pixel pour tracer.
pixel suivi d'un entier en pixel pour l'épaisseur du trait.
color suivi d'un entier à 9 chiffres (rrrgggbbb) pour la couleur du trait.
x, y suivis chacun d'un entier en pixel pour la position x ou y.
file suivi d'un nom de fichier pour sauvegarder votre oeuvre d'art ;-)

Une doc est incluse.

Ce code n'a aucune prétention, c'est juste pour le fun. Mais il a des vertus pédagogiques certaines...!

NB:
Ce code utilise la librairie js protoype pour faciliter le code js.
Ce code n'ea été testé QUE sous Firefox, et je doute qu'il tourne sous IE à cause de la récupération des textNode du flux xml (pas eu le temps de tester sous IE pour être franc).

Source / Exemple :


<?php
/**

  • Notre INTERFACE EXPRESSION
*
  • /
interface expression { public static function interpret(interpreter $subject, $mParam); } /**
  • Une EXPRESSION CONCRETE pour les mouvements vers la droite
*
  • /
class right implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid distance '.$mParam); } $mParam = (int)$mParam; if (false === imageline($subject->IMG, $subject->X, $subject->Y, $subject->X+$mParam, $subject->Y, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create line'); } $subject->X += $mParam; } } /**
  • Une EXPRESSION CONCRETE pour les mouvements vers la gauche
*
  • /
class left implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid distance '.$mParam); } $mParam = (int)$mParam; if (false === imageline($subject->IMG, $subject->X, $subject->Y, $subject->X-$mParam, $subject->Y, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create line'); } $subject->X -= $mParam; } } /**
  • Une EXPRESSION CONCRETE pour les mouvements vers le haut
*
  • /
class up implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid distance '.$mParam); } $mParam = (int)$mParam; if (false === imageline($subject->IMG, $subject->X, $subject->Y, $subject->X, $subject->Y-$mParam, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create line'); } $subject->Y -= $mParam; } } /**
  • Une EXPRESSION CONCRETE pour les mouvements vers le bas
*
  • /
class down implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid distance '.$mParam); } $mParam = (int)$mParam; if (false === imageline($subject->IMG, $subject->X, $subject->Y, $subject->X, $subject->Y+$mParam, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create line'); } $subject->Y += $mParam; } } /**
  • Une EXPRESSION CONCRETE pour la création d'une ellipse
*
  • /
class ellipse implements expression { public static function interpret(interpreter $subject, $mParam) { if(false === strpos($mParam, '-')) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid parameter '.$mParam); } $aParam = explode('-', $mParam); $iWidth = (int)$aParam[0]; $iHeight = (int)$aParam[1]; if (false === imageellipse($subject->IMG, $subject->X, $subject->Y, $iWidth, $iHeight, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create ellipse'); } $subject->Y += $mParam; } } /**
  • Une EXPRESSION CONCRETE pour la création d'une ellipse remplie
*
  • /
class filledellipse implements expression { public static function interpret(interpreter $subject, $mParam) { if(false === strpos($mParam, '-')) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid parameter '.$mParam); } $aParam = explode('-', $mParam); $iWidth = (int)$aParam[0]; $iHeight = (int)$aParam[1]; if (false === imagefilledellipse($subject->IMG, $subject->X, $subject->Y, $iWidth, $iHeight, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create filled ellipse'); } $subject->Y += $mParam; } } /**
  • Une EXPRESSION CONCRETE pour la création d'un arc remplis
*
  • /
class filledarc implements expression { public static function interpret(interpreter $subject, $mParam) { if(false === strpos($mParam, '-')) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid parameter '.$mParam); } $aParam = explode('-', $mParam); $iWidth = (int)$aParam[0]; $iHeight = (int)$aParam[1]; $iStart = (int)$aParam[2]; $iEnd = (int)$aParam[3]; if (false === imagefilledarc($subject->IMG, $subject->X, $subject->Y, $iWidth, $iHeight, $iStart, $iEnd, $subject->RCOLOR, IMG_ARC_PIE)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create filled arc'); } $subject->Y += $mParam; } } /**
  • Une EXPRESSION CONCRETE pour la création d'un rectangle
*
  • /
class rectangle implements expression { public static function interpret(interpreter $subject, $mParam) { if(false === strpos($mParam, '-')) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid parameter '.$mParam); } $aParam = explode('-', $mParam); $iX = (int)$aParam[0]; $iY = (int)$aParam[1]; if (false === imagerectangle($subject->IMG, $subject->X, $subject->Y, $iX, $iY, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create rectangle'); } $subject->Y += $mParam; } } /**
  • Une EXPRESSION CONCRETE pour la création d'un polygone
*
  • /
class polygon implements expression { public static function interpret(interpreter $subject, $mParam) { if(false === strpos($mParam, '-')) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid parameter '.$mParam); } $aParam = array_map ('intval', explode('-', $mParam)); $iCount = count($aParam); if(0 !== $iCount%2) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : total points must be even : '.$iCount); } $iCount /= 2; if (false === imagepolygon($subject->IMG, $aParam, $iCount, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to create polygon'); } $subject->Y += $mParam; } } /**
  • Une EXPRESSION CONCRETE pour la couleur du trait
*
  • /
class color implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam) || 9 !== strlen($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid color '.$mParam); } $iRed = (int)substr($mParam, 0, 3); $iGreen = (int)substr($mParam, 3, 3); $iBlue = (int)substr($mParam, 6, 3); $subject->RCOLOR = imagecolorallocate($subject->IMG, $iRed, $iGreen, $iBlue); $subject->SCOLOR = $mParam; if(false === $subject->RCOLOR) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to allocate color '.$mParam); } } } /**
  • Une EXPRESSION CONCRETE pour la position X
*
  • /
class x implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid position '.$mParam); } $mParam = (int)$mParam; $subject->X = $mParam; } } /**
  • Une EXPRESSION CONCRETE pour la position Y
*
  • /
class y implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid position '.$mParam); } $mParam = (int)$mParam; $subject->Y = $mParam; } } /**
  • Une EXPRESSION CONCRETE pour l'épaisseur du trait
*
  • /
class pixel implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid position '.$mParam); } $mParam = (int)$mParam; if (false === imagesetthickness($subject->IMG, $mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to set thickness '.$mParam); } $subject->SPIXEL = $mParam; } } /**
  • Une EXPRESSION CONCRETE pour initialiser la taille de la police de caractère (1-5)
*
  • /
class fontsize implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_numeric($mParam) || $mParam < 1 || $mParam > 5) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid font size '.$mParam); } $mParam = (int)$mParam; $subject->FONTSIZE = $mParam; } } /**
  • Une EXPRESSION CONCRETE pour l'écriture d'un texte
*
  • /
class text implements expression { public static function interpret(interpreter $subject, $mParam) { if(0 !== strpos($mParam, '"') || (strlen($mParam) - 1) !== strrpos($mParam, '"')) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid text value '.$mParam); } $mParam = trim($mParam, '"'); if (false === imagestring($subject->IMG, $subject->FONTSIZE, $subject->X, $subject->Y, $mParam, $subject->RCOLOR)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to write text '.$mParam); } } } /**
  • Une EXPRESSION CONCRETE pour la sauvegarde de l'image sous un nom particulier
*
  • /
class file implements expression { public static function interpret(interpreter $subject, $mParam) { if(!is_string($mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid filename '.$mParam); } if (false === imagepng($subject->IMG, $mParam)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : Failed to ssave image to file '.$mParam); } } } /**
  • Notre INTERPRETER
*
  • /
class interpreter { private $iX = 200; private $iY = 200; private $sColor; private $iWidth = 500; private $iHeight = 500; private $iThickNess = 1; private $iFontSize = 1; private $aDefaultDim = array(500,500); private $oExpressionStack; private $imh; private $colorh; private $aCor = array( 'X' => array('cor' => 'iX', 'type' => 'is_int', 'gettable' => true, 'settable' => true), 'Y' => array('cor' => 'iY', 'type' => 'is_int', 'gettable' => true, 'settable' => true), 'COLOR' => array('cor' => 'sColor', 'type' => 'is_string', 'gettable' => true, 'settable' => true), 'SCOLOR' => array('cor' => 'sColor', 'type' => 'is_string', 'gettable' => true, 'settable' => true), 'RCOLOR' => array('cor' => 'colorh', 'type' => 'is_int', 'gettable' => true, 'settable' => true), 'WIDTH' => array('cor' => 'iWidth', 'type' => 'is_int', 'gettable' => true, 'settable' => true), 'HEIGHT' => array('cor' => 'iHeight', 'type' => 'is_int', 'gettable' => true, 'settable' => true), 'PIXEL' => array('cor' => 'iThickNess', 'type' => 'is_int', 'gettable' => true, 'settable' => true), 'FONTSIZE' => array('cor' => 'iFontSize', 'type' => 'is_int', 'gettable' => true, 'settable' => true), 'SPIXEL' => array('cor' => 'iThickNess', 'type' => 'is_int', 'gettable' => true, 'settable' => true), 'IMG' => array('cor' => 'imh', 'type' => 'is_resource', 'gettable' => true, 'settable' => false) ); public function __construct($sFile = null) { if(!is_null($sFile)) { $this->imh = @imagecreatefrompng($sFile); if(!$this->imh) { throw new functionnalException(__CLASS__.'::'.__FUNCTION__.'() : '.$sFile.' has not been found'); } } else { $this->imh = imagecreatetruecolor($this->iWidth, $this->iHeight); imagecolorallocate($this->imh, 0, 0, 0); } } public function interpret ($sChaine) { if(!is_string($sChaine)) { throw new functionnalException(__CLASS__.'::'.__FUNCTION__.'() : Parameter must be a string'); } $this->oExpressionStack = new ArrayIterator(preg_split('`("[^"]+")|[\s]+`', $sChaine, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)); $this->evaluate(); } private function evaluate() { while($this->oExpressionStack->valid()) { $sToken = $this->oExpressionStack->current(); if(!class_exists($sToken)) { throw new languageException(__CLASS__.'::'.__FUNCTION__.'() : invalid expression '.$sToken); } $this->oExpressionStack->next(); $mParam = $this->oExpressionStack->current(); $interpreter = new ReflectionMethod($sToken, 'interpret'); $interpreter->invoke(null, $this, $mParam); $this->oExpressionStack->next(); } } public function getMove() { if(true === headers_sent()) { throw new functionnalException(__CLASS__.'::'.__FUNCTION__.'() : Unable to display image, headers already sent'); } header('Content-type: image/png'); imagepng($this->imh); } public function getSavedMove($sFile) { imagepng($this->imh, $sFile); } public function __get($sProp) { if(!array_key_exists($sProp, $this->aCor) || !isset($this->aCor[$sProp]['gettable']) || false === $this->aCor[$sProp]['gettable']) { throw new functionnalException(__CLASS__.'::'.__FUNCTION__.'() : Property '.$sProp.' is not gettable'); } $sThisProp = $this->aCor[$sProp]['cor']; return $this->$sThisProp; } public function __set($sProp, $mVal) { if(!array_key_exists($sProp, $this->aCor) || !isset($this->aCor[$sProp]['settable']) || false === $this->aCor[$sProp]['settable'] || !isset($this->aCor[$sProp]['type'])) { throw new functionnalException(__CLASS__.'::'.__FUNCTION__.'() : Property '.$sProp.' is not settable'); } $sFunc = $this->aCor[$sProp]['type']; if(!$sFunc($mVal)) { throw new functionnalException(__CLASS__.'::'.__FUNCTION__.'() : Value '.$mVal.' is not a valid value for '.$sProp.'; waited : '.substr($sFunc, 3)); } if($sProp === 'COLOR') { color::interpret($this, $mVal); $this->sColor = $mVal; } elseif($sProp === 'PIXEL') { pixel::interpret($this, $mVal); $this->iThickNess = (int)$mVal; } elseif($sProp === 'FONTSIZE') { fontsize::interpret($this, $mVal); $this->iFontSize = (int)$mVal; } else { $sThisProp = $this->aCor[$sProp]['cor']; $this->$sThisProp = $mVal; } } } ?>

Conclusion :


Juste pour dire parce que c'est toujours agréable : ce code a terminé 6ème des Innovation Awards de novembre 2007 sur phpclasses.org :-)

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.