#
# $Id: renderer.py,v 1.17 2004/03/08 23:30:05 mrnolta Exp $
#
# Copyright (C) 2000-2001 Mike Nolta <mrnolta@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
import libplot, math
raw = libplot
from tex2libplot import tex2libplot
## polygon clipping
def sh_inside( p, dim, boundary, side ):
return side*p[dim] >= side*boundary
def sh_intersection( s, p, dim, boundary ):
mid = not dim
g = 0.
if p[dim] != s[dim]:
g = (boundary - s[dim])/(p[dim] - s[dim])
q = [0,0]
q[dim] = boundary
q[mid] = s[mid] + g*(p[mid] - s[mid])
return q[0], q[1]
def sutherland_hodgman( polygon, dim, boundary, side ):
out = []
s = polygon[-1]
s_inside = sh_inside( s, dim, boundary, side )
for p in polygon:
p_inside = sh_inside( p, dim, boundary, side )
crosses = (p_inside and not s_inside) or \
(not p_inside and s_inside)
if crosses:
out.append( sh_intersection(s, p, dim, boundary) )
if p_inside:
out.append( p )
s = p
s_inside = p_inside
return out
class RendererState:
def __init__( self ):
self.current = {}
self.saved = []
def set( self, name, value ):
self.current[name] = value
def get( self, name, notfound=None ):
if self.current.has_key(name):
return self.current[name]
for i in range(len(self.saved)):
d = self.saved[i]
if d.has_key(name):
return d[name]
return notfound
def save( self ):
self.saved.insert( 0, self.current )
self.current = {}
def restore( self ):
self.current = self.saved.pop(0)
def _hexcolor( hextriplet, scale=1 ):
s = float(scale) / 0xff
r = s * ((hextriplet >> 16) & 0xff)
g = s * ((hextriplet >> 8) & 0xff)
b = s * ((hextriplet >> 0) & 0xff)
return r, g, b
def _set_color( pl, color ):
if type(color) == type(''):
raw.set_colorname_fg( pl, color )
else:
r,g,b = _hexcolor( color )
raw.set_color_fg( pl, r, g, b )
def _set_pen_color( pl, color ):
if type(color) == type(''):
raw.set_colorname_pen( pl, color )
else:
r,g,b = _hexcolor( color )
raw.set_color_pen( pl, r, g, b )
def _set_fill_color( pl, color ):
if type(color) == type(''):
raw.set_colorname_fill( pl, color )
else:
r,g,b = _hexcolor( color )
raw.set_color_fill( pl, r, g, b )
_pl_line_type = {
"dot" : "dotted",
"dash" : "shortdashed",
"dashed" : "shortdashed",
}
def _set_line_type( pl, type ):
pl_type = _pl_line_type.get( type, type )
raw.set_line_type( pl, pl_type )
class LibplotRenderer:
def __init__( self, ll, ur, type='X', parameters=None, file=None ):
self.lowerleft = ll
self.upperright = ur
self.pl = raw.new( type, parameters, file )
def open( self ):
self.state = RendererState()
raw.begin_page( self.pl )
apply( raw.space, \
(self.pl,) + self.lowerleft + self.upperright )
raw.clear( self.pl )
def clear( self ):
raw.clear( self.pl )
def close( self ):
if self.pl is not None:
raw.end_page( self.pl )
def delete( self ):
if self.pl is not None:
raw.delete( self.pl )
self.pl = None
def __del__( self ):
self.delete()
## state commands
__pl_style_func = {
"color" : _set_color,
"linecolor" : _set_pen_color,
"fillcolor" : _set_fill_color,
"linetype" : _set_line_type,
"linewidth" : raw.set_line_size,
"filltype" : raw.set_fill_level,
"fillmode" : raw.set_fill_type,
"fontface" : raw.set_font_type,
"fontsize" : raw.set_font_size,
"textangle" : raw.set_string_angle,
}
def set( self, key, value ):
self.state.set( key, value )
if LibplotRenderer.__pl_style_func.has_key(key):
method = LibplotRenderer.__pl_style_func[key]
apply( method, (self.pl, value) )
def get( self, parameter, notfound=None ):
return self.state.get( parameter, notfound )
def save_state( self ):
self.state.save()
raw.gsave( self.pl )
def restore_state( self ):
self.state.restore()
raw.grestore( self.pl )
## drawing commands
def move( self, p ):
raw.move( self.pl, p[0], p[1] )
def lineto( self, p ):
raw.lineto( self.pl, p[0], p[1] )
def linetorel( self, p ):
raw.linetorel( self.pl, p[0], p[1] )
def line( self, p, q ):
cr = self.get( "cliprect" )
if cr is None:
raw.line( self.pl, p[0], p[1], q[0], q[1] )
else:
raw.clipped_line( self.pl, \
cr[0], cr[1], cr[2], cr[3], \
p[0], p[1], q[0], q[1] )
def rect( self, p, q ):
raw.rect( self.pl, p[0], p[1], q[0], q[1] )
def circle( self, p, r ):
raw.circle( self.pl, p[0], p[1], r )
def ellipse( self, p, rx, ry, angle=0. ):
raw.ellipse( self.pl, p[0], p[1], rx, ry, angle )
def arc( self, c, p, q ):
raw.arc( self.pl, c[0], c[1], p[0], p[1], q[0], q[1] )
__pl_symbol_type = {
"none" : 0,
"dot" : 1,
"plus" : 2,
"asterisk" : 3,
"circle" : 4,
"cross" : 5,
"square" : 6,
"triangle" : 7,
"diamond" : 8,
"star" : 9,
"inverted triangle" : 10,
"starburst" : 11,
"fancy plus" : 12,
"fancy cross" : 13,
"fancy square" : 14,
"fancy diamond" : 15,
"filled circle" : 16,
"filled square" : 17,
"filled triangle" : 18,
"filled diamond" : 19,
"filled inverted triangle" : 20,
"filled fancy square" : 21,
"filled fancy diamond" : 22,
"half filled circle" : 23,
"half filled square" : 24,
"half filled triangle" : 25,
"half filled diamond" : 26,
"half filled inverted triangle" : 27,
"half filled fancy square" : 28,
"half filled fancy diamond" : 29,
"octagon" : 30,
"filled octagon" : 31,
}
def symbol( self, p ):
self.symbols( [p[0]], [p[1]] )
def symbols( self, x, y ):
DEFAULT_SYMBOL_TYPE = "square"
DEFAULT_SYMBOL_SIZE = 0.01
type_str = self.state.get( "symboltype", DEFAULT_SYMBOL_TYPE )
size = self.state.get( "symbolsize", DEFAULT_SYMBOL_SIZE )
if len(type_str) == 1:
type = ord(type_str[0])
else:
type = LibplotRenderer.__pl_symbol_type.get( type_str )
cr = self.get( "cliprect" )
if cr is None:
raw.symbols( self.pl, x, y, type, size )
else:
raw.clipped_symbols( self.pl, x, y, type, size,
cr[0], cr[1], cr[2], cr[3] )
def colored_symbols( self, x, y, c ):
DEFAULT_SYMBOL_TYPE = "square"
DEFAULT_SYMBOL_SIZE = 0.01
type_str = self.state.get( "symboltype", DEFAULT_SYMBOL_TYPE )
size = self.state.get( "symbolsize", DEFAULT_SYMBOL_SIZE )
if len(type_str) == 1:
type = ord(type_str[0])
else:
type = LibplotRenderer.__pl_symbol_type.get( type_str )
cr = self.get( "cliprect" )
if cr is None:
# This will cause an error: not written yet
raw.colored_symbols( self.pl, x, y, type, size )
else:
raw.clipped_colored_symbols( self.pl, x, y, c, type, size,
cr[0], cr[1], cr[2], cr[3] )
def density_plot( self, densgrid, ((xmin,ymin), (xmax,ymax)) ):
raw.density_plot( self.pl, densgrid,
xmin, xmax, ymin, ymax )
def color_density_plot( self, densgrid, ((xmin,ymin), (xmax,ymax)) ):
raw.color_density_plot( self.pl, densgrid,
xmin, xmax, ymin, ymax )
def curve( self, x, y ):
cr = self.get( "cliprect" )
if cr is None:
raw.curve( self.pl, x, y )
else:
raw.clipped_curve( self.pl, x, y,
cr[0], cr[1], cr[2], cr[3] )
def polygon( self, points ):
pts = points
cr = self.get( "cliprect" )
if cr is not None:
pts = sutherland_hodgman( pts, 0, cr[0], +1 )
pts = sutherland_hodgman( pts, 0, cr[1], -1 )
pts = sutherland_hodgman( pts, 1, cr[2], +1 )
pts = sutherland_hodgman( pts, 1, cr[3], -1 )
self.move( pts[0] )
map( self.lineto, pts[1:] )
## text commands
__pl_text_align = {
"center" : ord('c'),
"baseline" : ord('x'),
"left" : ord('l'),
"right" : ord('r'),
"top" : ord('t'),
"bottom" : ord('b'),
}
def text( self, p, str ):
plstr = tex2libplot( str )
hstr = self.state.get( "texthalign", "center" )
vstr = self.state.get( "textvalign", "center" )
hnum = LibplotRenderer.__pl_text_align.get( hstr )
vnum = LibplotRenderer.__pl_text_align.get( vstr )
raw.move( self.pl, p[0], p[1] )
raw.string( self.pl, hnum, vnum, plstr )
def textwidth( self, str ):
plstr = tex2libplot( str )
return raw.get_string_width( self.pl, plstr )
def textheight( self, str ):
return self.state.get( "fontsize" ) ## XXX: kludge?
class NonInteractiveScreenRenderer( LibplotRenderer ):
def __init__( self, width, height ):
ll = 0, 0
ur = width, height
parameters = {
"BITMAPSIZE": "%dx%d" % (width, height),
"VANISH_ON_DELETE": "no",
}
LibplotRenderer.__init__( self, ll, ur, "X", parameters )
class InteractiveScreenRenderer( LibplotRenderer ):
def __init__( self, width, height ):
ll = 0, 0
ur = width, height
parameters = {
"BITMAPSIZE": "%dx%d" % (width, height),
"VANISH_ON_DELETE": "yes",
}
LibplotRenderer.__init__( self, ll, ur, "X", parameters )
def close( self ):
raw.flush( self.pl )
def delete( self ):
raw.flush( self.pl )
_saved_screen_renderer = None
def ScreenRenderer( persistent=0, width=512, height=512 ):
if persistent == 1:
global _saved_screen_renderer
if _saved_screen_renderer is None:
_saved_screen_renderer \
= InteractiveScreenRenderer( width, height )
_saved_screen_renderer.clear()
return _saved_screen_renderer
else:
return NonInteractiveScreenRenderer( width, height )
def _str_size_to_pts( str ):
import re
m = re.compile(r"([\d.]+)([^\s]+)").match(str)
num_xx = float(m.group(1))
units = m.group(2)
# convert to postscipt pt = in/72
xx2pt = { "in":72, "pt":1, "mm":2.835, "cm":28.35 }
num_pt = int(num_xx*xx2pt[units])
return num_pt
class PSRenderer( LibplotRenderer ):
def __init__( self, file, paper="", width="", height="", **kw ):
ll = 0, 0
ur = _str_size_to_pts(width), _str_size_to_pts(height)
pagesize = "%s,xsize=%s,ysize=%s" % (paper,width,height)
for key,val in kw.items():
pagesize = pagesize +","+ key +"="+ val
parameters = { "PAGESIZE": pagesize }
LibplotRenderer.__init__( self, ll, ur, "ps", parameters, file )
class ImageRenderer( LibplotRenderer ):
def __init__( self, type, width, height, file ):
ll = 0, 0
ur = width, height
parameters = { "BITMAPSIZE": "%dx%d" % (width, height) }
LibplotRenderer.__init__( self, ll, ur, type, parameters, file )
|