connect('expose_event' , array($this, 'expose_event')); $this->connect('configure_event' , array($this, 'configure_event')); $this->connect('realize' , array($this, 'realize')); $this->set_events(Gdk::EXPOSURE_MASK); $this->_gc = null; $this->_realized = false; } function realize(){ $this->_realized = true; } function realized(){ return $this->_realized; } function configure_event($widget, $event) { return true; } function expose_event($widget, $event) { $this->redraw(); return false; } function set_color($color){ $col = $this->colormap()->alloc_color($color); if($col === false) return $this->foreground('black'); return $this->gc()->set_foreground($col); } function set_background_color($color){ $col = $this->colormap()->alloc_color($color); if($col === false) return $this->background('white'); return $this->gc()->set_background($col); } function colormap(){ return $this->gc()->get_colormap(); } function drawable(){ if(!$this->realized()){ trigger_error("Widget not realized ; can't get drawable"); return null; } return $this->window; } function gc(){ if(!$this->realized()){ trigger_error("Widget not realized ; can't get GC"); return null; } if($this->_gc == null) $this->_gc = new GdkGc($this->drawable()); return $this->_gc; } function width(){ return $this->allocation->width; } function height(){ return $this->allocation->height; } function w(){ return $this->width(); } function h(){ return $this->height(); } protected function draw_point($x, $y){ $this->drawable()->draw_point($this->gc(),$x, $y); } protected function draw_rectangle($filled, $x, $y, $w, $h){ $this->drawable()->draw_rectangle($this->gc(), $filled, $x, $y, $w, $h); } public function draw_line($x1, $y1, $x2, $y2){ $this->drawable()->draw_line($this->gc(), $x1, $y1, $x2, $y2); } public function draw_text($text, $x, $y, $color, $font=null){ $this->set_color($color); if($font == null) $font = $this->style->get_font(); @$this->drawable()->draw_string($font, $this->gc(), $x, $y, $text); } protected function queue_tainted(){ # $this->queue_draw(); } protected function clear($color = 'white'){ $this->set_color($color); $this->draw_rectangle($filled=true, 0,0, $this->w(), $this->h()); } protected function set_clip_rectangle($x, $y, $w, $h){ $rec = new GdkRectangle($x, $y, $w, $h); $this->gc()->set_clip_rectangle($rec); } } class BufferedGraph extends DirectGraph{ protected $_pixmap; function __construct(){ parent::__construct(); $this->_pixmap = null; } function realize(){ parent::realize(); $this->redraw(); } function configure_event($widget, $event) { $w = $this->width(); $h = $this->height(); # allocated a new pixmap with according to requested size ($this->allocation) $this->_pixmap = new GdkPixmap($this->window, $w, $h, -1); # clear pixmap (white color) $this->_pixmap->draw_rectangle($this->style->white_gc, true, 0, 0, $w, $h); $this->redraw(); return true; } function expose_event($widget, $event) { $this->window->draw_drawable($this->style->fg_gc[$this->state], $this->drawable(), $event->area->x, $event->area->y, $event->area->x, $event->area->y, $event->area->width, $event->area->height); return false; } function drawable(){ if(!$this->realized()){ trigger_error("Widget not realized ; can't get pixmap"); return null; } return $this->_pixmap; } protected function queue_tainted(){ $this->queue_draw(); } } class Plotter extends DirectGraph{ # class Plotter extends BufferedGraph{ protected $dots; protected $border; protected $left_margin; function __construct($dots = null){ parent::__construct(); $this->dots = $dots; $this->border=15; $this->left_margin=0; } protected function redraw(){ if(!$this->realized()) return false; if($this->dots != null){ $this->plot($this->dots, 'red'); # redraw full area (acts on queue stack for expose events) $this->queue_tainted(); } } public function plot(&$dots, $color){ if(count($dots) == 0) return false; if(!$this->realized()) return false; $this->set_color($color); $w = $this->w() - 2* + $this->border - $this->left_margin; $h = $this->h() - 2* + $this->border; $count = count($dots); static $has_scaled = false; static $y_span; if(!$has_scaled){ $min = min(array_values($dots)); $max = max(array_values($dots)); $y_span = 1.2 * ($max - $min); # for auto-scale $has_scaled = true; } $last = array(); foreach($dots as $key=>$value){ $x = intval($key * $w / $count) + $this->border + $this->left_margin; # auto-scale X $y = intval($value * $h / $y_span); # auto-scale Y # center Y value and translate $y = intval($h/2 - $y + $this->border ); if(count($last)>0) $this->draw_line($x, $y, $last[0], $last[1]); $last[0] = $x; $last[1] = $y; } return true; } } class MathPlotter extends Plotter{ function __construct($dots = null){ parent::__construct(); $this->left_margin=100; } protected function redraw(){ $this->set_clip_rectangle(0, 0, $this->w(), $this->h()); $this->clear(); $x = $y = $this->border; $w = $this->w() - $this->border*2; $h = $this->h() - $this->border*2; $this->set_clip_rectangle($x, $y, $w, $h); $plots = array(); # y = 100 * cos ( 5x ) for($i = 0 ; $i < 36*5 ; $i++){ $plots[$i] = 100 * cos(5 * $i / 360 * pi()); } $this->plot($plots, 'red'); # y = 50 * sin (5x) for($i = 0 ; $i < 36*5 ; $i++){ $plots[$i] = 50 * sin(5 * $i / 360 * pi()); } $this->plot($plots, 'green'); # y = 10 * tang(5x) for($i = 1 ; $i < 36*5 ; $i++){ $plots[$i] = 10 * tan(5 * $i / 360 * pi()); } $this->plot($plots, 'brown'); # y = 100 * sin( 0.1 x²) for($i = 0 ; $i < 36*5 ; $i++){ $plots[$i] = 100 * sin(0.1 * $i*$i / 360 * pi()); } $this->plot($plots, 'yellow'); $this->set_clip_rectangle(0, 0, $this->w(), $this->h()); $x = 10; $y = 30; $this->draw_text($text='10*tang(5x)', $x, $y, $color='brown'); $y +=15; $this->draw_text($text='100*cos(5x)', $x, $y, $color='red'); $y +=15; $this->draw_text($text='50*sin(5x)', $x, $y, $color='green'); $y +=15; $this->draw_text($text='100*sin(0.1x²)', $x, $y, $color='yellow'); $y +=15; } } $plotter = new MathPlotter(); $plotter->set_size_request(150/2, 100/2); $window = new GtkWindow(); $window->connect_simple('destroy', array('gtk', 'main_quit')); $window->set_title('Plot demo'); $window->set_position(Gtk::WIN_POS_CENTER); $window->set_border_width(8); $window->add($plotter); $window->show_all(); Gtk::main(); ?>