From 0c9244a44b1460e05f716bc469898f309d850996 Mon Sep 17 00:00:00 2001 From: vidon Date: Mon, 13 Jun 2005 00:25:58 +0000 Subject: Ajout de la lib Graphics --- Python/library/__init__.py | 4 + Python/library/geometry.py | 410 ++++++++ Python/library/graphics.py | 2351 +++++++++++++++++++++++++++++++++++++++++++ Python/library/pictorial.py | 766 ++++++++++++++ 4 files changed, 3531 insertions(+) create mode 100644 Python/library/__init__.py create mode 100644 Python/library/geometry.py create mode 100644 Python/library/graphics.py create mode 100644 Python/library/pictorial.py diff --git a/Python/library/__init__.py b/Python/library/__init__.py new file mode 100644 index 0000000..d8e4f37 --- /dev/null +++ b/Python/library/__init__.py @@ -0,0 +1,4 @@ +from Zinc import * +import graphics +import geometry +import pictorial diff --git a/Python/library/geometry.py b/Python/library/geometry.py new file mode 100644 index 0000000..4063b61 --- /dev/null +++ b/Python/library/geometry.py @@ -0,0 +1,410 @@ +# -*- coding: iso-8859-1 -*- +""" +# Geometrical basic Functions : +# ----------------------------- +# perpendicular_point +# line_angle +# linenormal +# vertex_angle +# arc_pts +# rad_point +# bezier_compute +# bezier_segment +# bezier_point +""" + +from math import pi, radians, atan2, sin, cos + +# limite globale d'approximation courbe bezier +bezierClosenessThreshold = .2 +def perpendicular_point (point, line): + """ + #--------------------------------------------------------------------------- + # Graphics::perpendicular_point + # retourne les coordonnées du point perpendiculaire abaissé d'un point + # sur une ligne + #--------------------------------------------------------------------------- + # paramètres : + # point : coordonnées du point de référence + # line : coordonnées des 2 points de la ligne de référence + #--------------------------------------------------------------------------- + """ + (p1, p2) = line + + # cas partiuculier de lignes ortho. + min_dist = .01 + if (abs(p2[1] - p1[1]) < min_dist) : + # la ligne de référence est horizontale + return (point[0], p1[1]) + + elif (abs(p2[0] - p1[0]) < min_dist) : + # la ligne de référence est verticale + return (p1[0], point[1]) + + a1 = float(p2[1] - p1[1]) / float(p2[0] - p1[0]) + b1 = p1[1] - (a1 * p1[0]) + + a2 = -1.0 / a1 + b2 = point[1] - (a2 * point[0]) + + x = (b2 - b1) / (a1 - a2) + y = (a1 * x) + b1 + + return (x, y) + +def line_angle(startpoint, endpoint): + """ + #--------------------------------------------------------------------------- + # Graphics::line_angle + # retourne l'angle d'un point par rapport à un centre de référence + #--------------------------------------------------------------------------- + # paramètres : + # startpoint : coordonnées du point de départ du segment + # endpoint : coordonnées du point d'extremité du segment + #--------------------------------------------------------------------------- + """ + angle = atan2(endpoint[1] - startpoint[1], endpoint[0] - startpoint[0]) + + angle += pi/2 + angle *= float(180)/pi + if (angle < 0): + angle += 360 + + return angle + +def linenormal(startpoint, endpoint): + """ + #--------------------------------------------------------------------------- + # Graphics::linenormal + # retourne la valeur d'angle perpendiculaire à une ligne + #--------------------------------------------------------------------------- + # paramètres : + # startpoint : coordonnées du point de départ du segment + # endpoint : coordonnées du point d'extremité du segment + #--------------------------------------------------------------------------- + """ + angle = line_angle(startpoint, endpoint) + 90 + + if (angle > 360): + angle -= 360 + return angle + +def vertex_angle(pt0, pt1, pt2): + """ + #--------------------------------------------------------------------------- + # Graphics::vertex_angle + # retourne la valeur de l'angle formée par 3 points + # ainsi que l'angle de la bisectrice + #--------------------------------------------------------------------------- + # paramètres : + # pt0 : coordonnées du premier point de définition de l'angle + # pt1 : coordonnées du deuxième point de définition de l'angle + # pt2 : coordonnées du troisième point de définition de l'angle + #--------------------------------------------------------------------------- + """ + angle1 = line_angle(pt0, pt1) + angle2 = line_angle(pt2, pt1) + + if angle2 < angle1 : + angle2 += 360 + alpha = angle2 - angle1 + bisectrice = angle1 + (float(alpha)/2) + + return (alpha, bisectrice) + + +def arc_pts(center, radius, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::arc_pts + # calcul des points constitutif d'un arc + #--------------------------------------------------------------------------- + # paramètres : + # center : centre de l'arc, + # radius : rayon de l'arc, + # options : + # -angle : angle de départ en degré de l'arc (par défaut 0) + # -extent : delta angulaire en degré de l'arc (par défaut 360), + # -step : pas de progresion en degré (par défaut 10) + #--------------------------------------------------------------------------- + """ + if center is None : + center = [0, 0] + if (options.has_key('angle')) : + angle = options['angle'] + else: + angle = 0 + if (options.has_key('extent')) : + extent = options['extent'] + else: + extent = 360 + if (options.has_key('step')) : + step = options['step'] + else: + step = 10 + pts = [] + + if (extent > 0 and step > 0) : + #A Verifier ! + alpha = angle + while(alpha <= angle+extent): + (x_n, y_n) = rad_point(center, radius, alpha) + pts.append((x_n, y_n)) + angle += step + + elif (extent < 0 and step < 0) : + #Ca me semble buggue !! + #Si extent négatif, il faut aussi que step le soit + #Si ca boucle ! + alpha = angle + while(alpha >= angle+extent): + pts.append(rad_point(center, radius, alpha)) + alpha += step + else: + raise ValueError("Step and Extent havent the same sign") + return tuple(pts) + +def rad_point(center, radius, angle): + """ + #--------------------------------------------------------------------------- + # Graphics::rad_point + # retourne le point circulaire défini par centre-rayon-angle + #--------------------------------------------------------------------------- + # paramètres : + # center : coordonnée [x,y] du centre de l'arc, + # radius : rayon de l'arc, + # angle : angle du point de circonférence avec le centre du cercle + #--------------------------------------------------------------------------- + """ + alpha = radians(angle) + + xpt = center[0] + (radius * cos(alpha)) + ypt = center[1] + (radius * sin(alpha)) + + return (xpt, ypt) + +def bezier_segment(coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::bezier_segment + # Calcul d'une approximation de segment (Quadratique ou Cubique) de bezier + #--------------------------------------------------------------------------- + # paramètres : + # points : <[P1, C1, , P2]> liste des points définissant + # le segment de bezier + # + # options : + # -tunits : nombre pas de division des segments bezier + # (par défaut 20) + # -skipend : : ne pas retourner le dernier point du + # segment (chainage) + #--------------------------------------------------------------------------- + """ + if (options.has_key('tunits')) : + tunits = options['tunits'] + else: + tunits = 20 + + + if options.has_key('skipend'): + skipendpt = options['skipend'] + else: + skipendpt = None + + pts = [] + + if (skipendpt) : + lastpt = tunits-1 + else: + lastpt = tunits + for i in xrange(0, lastpt+1): + if (i) : + t = (i/tunits) + else: + t = i + pts.append(bezier_point(t, coords)) + + return pts + + +def bezier_point(t, coords): + """ + #--------------------------------------------------------------------------- + # Graphics::bezier_point + # calcul d'un point du segment (Quadratique ou Cubique) de bezier + # params : + # t = (représentation du temps : de 0 à 1) + # coords = (P1, C1, , P2) liste des points définissant le segment + # de bezier P1 et P2 : extémités du segment et pts situés sur la courbe + # C1 : point(s) de contrôle du segment + #--------------------------------------------------------------------------- + # courbe bezier niveau 2 sur (P1, P2, P3) + # P(t) = (1-t)²P1 + 2t(1-t)P2 + t²P3 + # + # courbe bezier niveau 3 sur (P1, P2, P3, P4) + # P(t) = (1-t)³P1 + 3t(1-t)²P2 + 3t²(1-t)P3 + t³P4 + #--------------------------------------------------------------------------- + """ + ncoords = len(coords) + if ncoords == 3: + (p1, c1, p2) = coords + c2 = None + elif ncoords == 4: + (p1, c1, c2, p2) = coords + + # extrémités : points sur la courbe + #A VERIFIER + #Pas compris + if (not t): + return tuple(p1) + if (t >= 1.0): + return p2 + + + t2 = t * t + t3 = t2 * t + pt = [] + + # calcul pour x et y + for i in (0, 1) : + + if (c2) : + r1 = (1 - (3*t) + (3*t2) - t3) * p1[i] + r2 = ((3*t) - (6*t2) + (3*t3)) * c1[i] + r3 = ((3*t2) - (3*t3)) * c2[i] + r4 = (t3) * p2[i] + + pt[i] = (r1 + r2 + r3 + r4) + + else : + r1 = (1 - (2*t) + t2) * p1[i] + r2 = ((2*t) - (2*t2)) * c1[i] + r3 = (t2) * p2[i] + + pt[i] = (r1 + r2 + r3) + + return tuple(pt) + +def bezier_compute(coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::bezier_compute + # Retourne une liste de coordonnées décrivant un segment de bezier + #--------------------------------------------------------------------------- + # paramètres : + # coords : liste des points définissant le segment + # de bezier + # + # options : + # -precision : seuil limite du calcul d'approche de la courbe + # -skipend : : ne pas retourner le dernier point du segment + # (chaînage bezier) + #--------------------------------------------------------------------------- + """ + if (options.has_key('precision')) : + precision = options['precision'] + else: + precision = bezierClosenessThreshold + lastit = [] + + subdivide_bezier(coords, lastit, precision) + + if (not options.has_key('skipend') or not options['skipend']): + lastit.append(coords[3]) + + return lastit + +def smallenough_bezier(bezier, precision): + """ + #--------------------------------------------------------------------------- + # Graphics::smallEnought + # intégration code Stéphane Conversy : calcul points bezier + # (précision auto ajustée) + #--------------------------------------------------------------------------- + # distance is something like num/den with den=sqrt(something) + # what we want is to test that distance is smaller than precision, + # so we have distance < precision ? eq. to distance^2 < precision^2 ? + # eq. to (num^2/something) < precision^2 ? + # eq. to num^2 < precision^2*something + # be careful with huge values though (hence 'long long') + # with common values: 9add 9mul + #--------------------------------------------------------------------------- + """ + (pt_x, pt_y) = (0, 1) + (a, b) = (bezier[0], bezier[3]) + + den = ((a[pt_y]-b[pt_y])*(a[pt_y]-b[pt_y])) + ((b[pt_x]-a[pt_x])*(b[pt_x]-a[pt_x])) + p = precision*precision + + # compute distance between P1|P2 and P0|P3 + mat = bezier[1] + num1 = ((mat[pt_x]-a[pt_x])*(a[pt_y]-b[pt_y])) + ((mat[pt_y]-a[pt_y])*(b[pt_x]-a[pt_x])) + + mat = bezier[2] + num2 = ((mat[pt_x]-a[pt_x])*(a[pt_y]-b[pt_y])) + ((mat[pt_y]-a[pt_y])*(b[pt_x]-a[pt_x])) + + # take the max + num1 = max(num1, num2) + + if (p*den > (num1*num1)): + return 1 + else: + return 0 + +def subdivide_bezier(bezier, it, precision, integeropt): + """ + #--------------------------------------------------------------------------- + # Graphics::subdivide_bezier + # subdivision d'une courbe de bezier + #--------------------------------------------------------------------------- + """ + (b0, b1, b2, b3) = bezier + + if (smallenough_bezier(bezier, precision)) : + it.append((b0[0], b0[1])) + + else : + (left, right) = (None, None) + + for i in (0, 1) : + + if (integeropt) : + # int optimized (6+3=9)add + (5+3=8)shift + + left[0][i] = b0[i] + left[1][i] = (b0[i] + b1[i]) >> 1 + # keep precision + left[2][i] = (b0[i] + b2[i] + (b1[i] << 1)) >> 2 + + tmp = (b1[i] + b2[i]) + left[3][i] = (b0[i] + b3[i] + (tmp << 1) + tmp) >> 3 + + right[3][i] = b3[i] + right[2][i] = (b3[i] + b2[i]) >> 1 + # keep precision + right[1][i] = (b3[i] + b1[i] + (b2[i] << 1) ) >> 2 + right[0][i] = left[3][i] + + else : + # float + + left[0][i] = b0[i] + left[1][i] = float(b0[i] + b1[i]) / 2 + left[2][i] = float(b0[i] + (2*b1[i]) + b2[i]) / 4 + left[3][i] = float(b0[i] + (3*b1[i]) + (3*b2[i]) + b3[i]) / 8 + + right[3][i] = b3[i] + right[2][i] = float(b3[i] + b2[i]) / 2 + right[1][i] = float(b3[i] + (2*b2[i]) + b1[i]) / 4 + right[0][i] = float(b3[i] + (3*b2[i]) + (3*b1[i]) + b0[i]) / 8 + + + + subdivide_bezier(left, it, precision, integeropt) + subdivide_bezier(right, it, precision, integeropt) + + +#Local Variables: +#mode : python +#tab-width: 4 +#end: diff --git a/Python/library/graphics.py b/Python/library/graphics.py new file mode 100644 index 0000000..20f68c7 --- /dev/null +++ b/Python/library/graphics.py @@ -0,0 +1,2351 @@ +# -*- coding: iso-8859-1 -*- +""" +#------------------------------------------------------------------------------- +# +# Graphics.py +# some graphic design functions +# +#------------------------------------------------------------------------------- +# Functions to create complexe graphic component : +# ------------------------------------------------ +# build_zinc_item (realize a zinc item from description hash table +# management of enhanced graphics functions) +# +# repeat_zinc_item (duplication of given zinc item) +# +# Function to compute complexe geometrical forms : +# (text header of functions explain options for each form, +# function return curve coords using control points of cubic curve) +# ----------------------------------------------------------------- +# rounded_rectangle_coords (return curve coords of rounded rectangle) +# hippodrome_coords (return curve coords of circus form) +# ellipse_coords (return curve coords of ellipse form) +# polygon_coords (return curve coords of regular polygon) +# roundedcurve_coords (return curve coords of rounded curve) +# polyline_coords (return curve coords of polyline) +# shiftpath_coords (return curve coords of shifting path) +# tabbox_coords (return curve coords of tabbox's pages) +# pathline_coords (return triangles coords of pathline) +# +# Function to compute 2D 1/2 relief and shadow : +# function build zinc items (triangles and curve) to simulate this +# ----------------------------------------------------------------- +# graphicitem_relief (return triangle items simulate relief of given item) +# polyline_relief_params (return triangle coords +# and lighting triangles color list) +# graphicitem_shadow (return triangles and curve items +# simulate shadow of given item)) +# polyline_shadow_params (return triangle and curve coords +# and shadow triangles color list)) +# +# +#------------------------------------------------------------------------------- +# Authors: Jean-Luc Vinot +# PM2PY: Guillaume Vidon +# +# $Id$ +#------------------------------------------------------------------------------- +""" +VERSION = "1.0" +__revision__ = "0.1" + +import logging +import types +import re +from geometry import * +from pictorial import * +from math import pi, radians, atan2, sqrt, sin, cos + +graphiclogger = logging.getLogger('Graphics') +debug = graphiclogger.debug +error = graphiclogger.error +exception = graphiclogger.exception +log = lambda msg : graphiclogger.log(logging.DEBUG, msg) + +# constante facteur point directeur (conique -> quadratique) +const_ptd_factor = .5523 + +def transdic(**dict): + newdic={} + for key, val in dict.items(): + if (type(val) is types.ListType): + newdic[key] = tuple(val) + else: + newdic[key] = val + return newdic + +def is_flat_list(apts): + if reduce(lambda x, y : x and (type(y) in ( types.FloatType, types.IntType)), + apts): + if len(apts) % 2: + raise ValueError("Not a valid Coords list") + else : + return True + else : + return False + +def is_point(apoint): + if (type(apoint) in ( types.TupleType, types.ListType)): + if len(apoint) == 2 : + if reduce(lambda x, y : x and (type(y) in ( types.FloatType, types.IntType)), + apts): + return True + else : + return False + elif reduce(lambda x, y : x and (type(y) in ( types.FloatType, types.IntType)), + apts[:-1])\ + and type(apts[-1]) in ('c', 'n'): + return True + else : + return False + else : + return False + +# def is_tuple_list(apts): +# if reduce(lambda x, y : x \ +# and is_point(x)), +# apts): + + +def lpts2coords(lpts): + coords = [] + if (type(lpts) in ( types.TupleType, types.ListType )): + for point in lpts : + coords.append(tuple(point)) + return tuple(coords) + +def coords2lpts(coords): + lpts = [] + if (type(coords) in ( types.TupleType, types.ListType )): + for point in coords : + lpts.append(list(point)) + else : + raise ValueError("Invalid Coords %s "%coords) + return lpts + +def build_zinc_item(widget, pgroup = 1, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::build_zinc_item + # Création d'un objet Zinc de représentation + #--------------------------------------------------------------------------- + # types d'items valides : + # les items natifs zinc : group, rectangle, arc, curve, text, icon + # les items ci-après permettent de spécifier des curves 'particulières' : + # -roundedrectangle : rectangle à coin arrondi + # -hippodrome : hippodrome + # -ellipse : ellipse un centre 2 rayons + # -polygone : polygone régulier à n cotés (convexe ou en étoile) + # -roundedcurve : curve multicontours à coins arrondis (rayon unique) + # -polyline : curve multicontours à coins arrondis (le rayon pouvant + # être défini + # spécifiquement pour chaque sommet) + # -pathline : création d'une ligne 'épaisse' avec l'item Zinc + # triangles décalage par rapport à un chemin donné + # (largeur et sens de décalage) + # dégradé de couleurs de la ligne (linéaire, transversal + # ou double) + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget Zinc + # parentgroup : identifiant du group parent + # + # options : + # -itemtype : type de l'item à construire (type zinc ou metatype) + # -coords : coordonnées de l'item + # -metacoords : calcul de coordonnées par type + # d'item différent de itemtype + # -contours : paramètres multi-contours + # -params : arguments spécifiques de l'item à passer + # au widget + # -addtags : [list of specific tags] to add to params -tags + # -texture : ajout d'une texture à l'item + # -pattern : ajout d'un pattern à l'item + # -relief : création d'un relief à l'item invoque la fonction + # graphicitem_relief() + # -shadow : création d'une ombre portée à l'item invoque + # la fonction graphicitem_shadow() + # -scale : application + # d'une transformation zinc->scale à l'item + # -translate : <[delta_x,delta_y]> application d'un transformation zinc->translate + # à l'item. + # -rotate : application d'une transformation zinc->rotate + # (en degré) à l'item + # -name : nom de l'item + # spécifiques item group : + # -clip : paramètres de clipping d'un item group + # (coords ou item) + # -items : appel récursif de la fonction permettant + # d'inclure des items au groupe + #--------------------------------------------------------------------------- + # + #--------------------------------------------------------------------------- + """ + + if options.has_key('parentgroup'): + parentgroup = options['parentgroup'] + else : + parentgroup = pgroup + try: + itemtype = options['itemtype'] + except KeyError: + raise ValueError("Must have itemtype option") + try: + coords = options['coords'] + except KeyError: + try: + coords = options['metacoords'] + except KeyError : + raise ValueError("Must have coords or metacoords option") + + if options.has_key('params'): + params = options['params'] + else: + params = {} + + #return unless($widget and $itemtype + #and ($coords or $options{'-metacoords'})) + + try: + name = options['name'] + except KeyError: + name = None + + item = None + metatype = None + items = [] + reliefs = [] + shadows = [] + tags = [] + + #-------------------- + # GEOMETRIE DES ITEMS + + # gestion des types d'items particuliers et à raccords circulaires + if (itemtype in ( 'roundedrectangle', + 'hippodrome', + 'polygone', + 'ellipse', + 'roundedcurve', + 'polyline', + 'curveline')): + + # par défaut la curve sera fermée -closed = 1 + if not params.has_key('closed'): + params['closed'] = 1 + metatype = itemtype + itemtype = 'curve' + + # possibilité de définir les coordonnées initiales par metatype + if (options.has_key('metacoords')) : + options['coords'] = meta_coords( **options['metacoords']) + + # création d'une pathline à partir d'item zinc triangles + elif (itemtype == 'pathline') : + itemtype = 'triangles' + if (options.has_key('metacoords')) : + coords = meta_coords( **options['metacoords']) + + if (options.has_key('graduate')) : + numcolors = len(coords) + lcolors = path_graduate(widget, + numcolors, + options['graduate']) + params['colors'] = tuple(lcolors) + + if options.has_key('coords'): + coords = pathline_coords(**options) + else: + coords = pathline_coords(coords, **options) + + # création d'une boite à onglet + elif (itemtype == 'tabbox') : + return build_tabboxitem(widget, parentgroup, **options) + + # calcul des coordonnées finales de la curve + if (metatype is not None): + coords = meta_coords(type = metatype, **options) + + + # gestion du multi-contours (accessible pour tous les types + # d'items géometriques) + if (options.has_key('contours') and (metatype is not None)) : + lcontours = options['contours'] + contours=[] + numcontours = len(contours) + for contour in lcontours: + # radius et corners peuvent être défini + # spécifiquement pour chaque contour + (atype, way, addcoords,) = contour[:3] + if len(contour) >= 4: + radius = contour[3] + else : + radius = None + if len(contour) >= 5: + corners = contour[4] + else: + corners = None + if len(contour) >= 6: + corners_radius = contour[5] + else : + corners_radius = None + if (radius is None): + if options.has_key('radius'): + radius = options['radius'] + else : + raise ValueError("radius option requiered") + + newcoords = meta_coords(type = metatype, + coords = addcoords, + radius = radius, + corners = corners, + corners_radius = corners_radius + ) + contours.append((atype, way, newcoords)) + + options['contours'] = contours + + #---------------------- + # REALISATION DES ITEMS + + # ITEM GROUP + # gestion des coordonnées et du clipping + if (itemtype == 'group') : + item = widget.add(itemtype, + parentgroup, + **params) + widget.addtag_withtag(name, item) + if coords: + widget.coords(item, tuple(coords)) + + # clipping du groupe par item ou par géometrie + if (options.has_key('clip')) : + clipbuilder = options['clip'] + clip = None + + # création d'un item de clipping + if (type(clipbuilder) is types.DictType + and clipbuilder.has_key('itemtype')): + clip = build_zinc_item(widget, item, **clipbuilder) + + elif (type(clipbuilder) in (types.TupleType, types.ListType) + or widget.type(clipbuilder)) : + clip = clipbuilder + + if (clip): + widget.itemconfigure(item, clip = clip) + + # créations si besoin des items contenus dans le groupe + if (options.has_key('items') + and type(options['items']) is types.DictType) : + for (itemname, itemstyle) in options['items'].items() : + if not itemstyle.has_key('name'): + itemstyle['name'] = itemname + build_zinc_item(widget, item, **itemstyle) + + + # ITEM TEXT ou ICON + elif (itemtype in ('text', 'icon')) : + imagefile = None + if (itemtype == 'icon') : + imagefile = params['image'] + image = get_image(widget, imagefile) + if (image) : + params['image'] = image + else: + params['image'] = "" + + + item = widget.add(itemtype, + parentgroup, + position = coords, + **params + ) + if imagefile: + params['image'] = imagefile + + + # ITEMS GEOMETRIQUES -> CURVE + else : + nparams=params + item = widget.add(itemtype, + parentgroup, + lpts2coords(coords), + **params + ) + + if (itemtype == 'curve' and options.has_key('contours')) : + for contour in options['contours'] : + contour = list(contour) + contour[2] = tuple(contour[2]) + widget.contour(item, *contour) + + # gestion du mode norender + if (options.has_key('texture')) : + texture = get_texture(widget, options['texture']) + if texture: + widget.itemconfigure(item, tile = texture) + + if (options.has_key('pattern')) : + bitmap = get_pattern(**options['pattern']) + if bitmap: + widget.itemconfigure(item, fillpattern = bitmap) + + # gestion des tags spécifiques + if (options.has_key('addtags')) : + tags = options['addtags'] + + params_tags = params['tags'] + if params_tags: + tags.extend(params_tags) + + widget.itemconfigure(item, tags = tags) + + #------------------------------- + # TRANSFORMATIONS ZINC DE L'ITEM + + # transformation scale de l'item si nécessaire + if (options.has_key('scale')) : + scale = options['scale'] + if (type(scale) is not types.TupleType) : + scale = (scale, scale) + widget.scale(item, scale) + + + # transformation rotate de l'item si nécessaire + if (options.has_key('rotate')): + widget.rotate(item, radians(options['rotate'])) + + # transformation translate de l'item si nécessaire + if (options.has_key('translate')): + widget.translate(item, options['translate']) + + + # répétition de l'item + if (options.has_key('repeat')) : + items.extend((item, + repeat_zinc_item(widget, item, **options['repeat']))) + + #----------------------- + # RELIEF ET OMBRE PORTEE + + # gestion du relief + if (options.has_key('relief')) : + if (len(items)) : + target = items + else: + target = item + reliefs.extend(graphicitem_relief(widget, + target, **options['relief'])) + + # gestion de l'ombre portée + if (options.has_key('shadow')) : + if (len(items)) : + target = items + else: + target = item + shadows.extend(graphicitem_shadow(widget, + target, **options['shadow'])) + + + if len(reliefs): + items.extend(reliefs) + if len(shadows): + items.extend(shadows) + + if len(items): + return items + else: + return item + +def repeat_zinc_item(widget, + item, + num = 2, + dxy = (0,0), + angle = None, + params = None, + copytag = None) : + """ + #--------------------------------------------------------------------------- + # Graphics::repeat_zinc_item + # Duplication (clonage) d'un objet Zinc de représentation + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # item : identifiant de l'item source + # options : + # -num : nombre d'item total (par defaut 2) + # -dxy : <[delta_x, delta_y]> translation entre 2 duplications (par defaut [0,0]) + # -angle : rotation entre 2 duplications + # -copytag : ajout d'un tag indexé pour chaque copie + # -params : {clef => [value list]}> valeur de paramètre + # de chaque copie + #--------------------------------------------------------------------------- + """ + clones = [] + delta_x, delta_y = dxy + # duplication d'une liste d'items -> appel récursif + if (type(item) in (types.TupleType, types.ListType)) : + for part in item : + clones.append(repeat_zinc_item(widget, + part, + dxy, + angle, + params, + copytag)) + + return clones + + tags = [] + + if (copytag) : + tags = widget.itemcget(item, 'tags') + widget.itemconfigure(item, tags = tags + ("%s0"%copytag,)) + + for i in xrange(1, num) : + clone = None + + if (copytag) : + clone = widget.clone(item, tags = tags + ("%s%s"%(copytag, i),)) + else : + clone = widget.clone(item) + + clones.append(clone) + widget.translate(clone, delta_x*i, delta_y*i) + if angle : + widget.rotate(clone, radians(angle*i)) + + if (params is not None ) : + widget.itemconfigure(clone, **params ) + return clones + + +#MUST BE TESTED +def meta_coords( type, + coords, + **options ): + """ + #--------------------------------------------------------------------------- + # FONCTIONS GEOMETRIQUES + #--------------------------------------------------------------------------- + + #--------------------------------------------------------------------------- + # Graphics::meta_coords + # retourne une liste de coordonnées en utilisant la fonction du type + # d'item spécifié + #--------------------------------------------------------------------------- + # paramètres : (passés par %options) + # -type : type de primitive utilisée + # -coords : coordonnées nécessitée par la fonction [type]_coords + # + # les autres options spécialisées au type seront passés + # à la fonction [type]coords + #--------------------------------------------------------------------------- + """ + pts = None + + if (type == 'roundedrectangle'): + log('Coords for roundedrectangle') + pts = rounded_rectangle_coords(coords, **options) + + elif (type == 'hippodrome') : + log('Coords for hippodrome') + pts = hippodrome_coords(coords, **options) + + elif (type == 'ellipse') : + log('Coords for ellipse') + pts = ellipse_coords(coords, **options) + + elif (type == 'roundedcurve') : + log('Coords for roundedcurve') + pts = roundedcurve_coords(coords, **options) + + elif (type == 'polygone') : + log('Coords for polygone') + pts = polygon_coords(coords, **options) + + elif (type == 'polyline') : + log('Coords for polyline') + pts = polyline_coords(coords, **options) + + elif (type == 'curveline') : + log('Coords for curveline') + pts = curveline_coords(coords, **options) + + return pts + + +def zincitem_2_curvecoords( widget, item, + linear = 0, + realcoords = 0, + adjust = 1, + ): + """ + #-------------------------------------------------------------------------- + # Graphics::zincitem_2_curvecoords + # retourne une liste des coordonnées 'Curve' d'un l'item Zinc + # rectangle, arc ou curve + #-------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # item : identifiant de l'item source + # options : + # -linear : réduction à des segments non curviligne + # (par défaut 0) + # -realcoords : coordonnées à transformer dans le groupe père + # (par défaut 0) + # -adjust : ajustement de la courbe de bezier (par défaut 1) + #-------------------------------------------------------------------------- + """ + itemtype = widget.type(item) + + if not itemtype : + raise ValueError("Not a Valid Item %s" % item) + + itemcoords = widget.coords(item) + coords = None + multi = [] + + if (itemtype == 'rectangle') : + coords = rounded_rectangle_coords(itemcoords, radius = 0) + + elif (itemtype == 'arc') : + coords = ellipse_coords(itemcoords) + if linear : + coords = curve2polyline_coords(coords, adjust) + + elif (itemtype == 'curve') : + numcontours = widget.contour(item) + + if (numcontours < 2) : + if linear: + coords = curve2polyline_coords(itemcoords, adjust) + else : + if (linear) : + multi = curveitem2polyline_coords(widget, item) + + else : + for contour in xrange(0, numcontours): + points = widget.coords(item, contour) + multi.extend(points) + coords = multi + + if (realcoords) : + parentgroup = widget.group(item) + if (len(multi)) : + newcoords = [] + for points in multi : + transcoords = widget.transform(item, parentgroup, points) + newcoords.extend(transcoords) + coords = newcoords + else : + transcoords = widget.transform(item, parentgroup, coords) + coords = transcoords + + return coords + +def rounded_rectangle_coords( coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::rounded_rectangle_coords + # calcul des coords du rectangle à coins arrondis + #--------------------------------------------------------------------------- + # paramètres : + # coords : coordonnées bbox (haut-gauche et bas-droite) + # du rectangle + # options : + # -radius : rayon de raccord d'angle + # -corners : liste des raccords de sommets + # [0 (aucun raccord)|1] par défaut [1,1,1,1] + #--------------------------------------------------------------------------- + """ + (x_0, y_0, x_n, y_n) = (coords[0][0], coords[0][1], + coords[1][0], coords[1][1]) + + if (options.has_key('radius')): + radius = options['radius'] + else: + radius = None + + if (options.has_key('corners')): + corners = options['corners'] + else: + corners = [1, 1, 1, 1] + + # attention aux formes 'négatives' + if (x_n < x_0) : + (x_0, x_n) = (x_n, x_0) + + if (y_n < y_0) : + (y_0, y_n) = (y_n, y_0) + + height = min(x_n -x_0, y_n - y_0) + #radius non defini dans les parametres + if (radius is None) : + radius = int(height/10) + radius = max(radius, 3) + + #radius defini mais trop petit + if ( radius < 2) : + return ((x_0, y_0), (x_0, y_n), (x_n, y_n), (x_n, y_0)) + + # correction de radius si necessaire + max_rad = height + #CODE BIZARRE + #Comment corners ne peut être non défini + #a ce niveau ? + # max_rad /= 2 if (!defined corners) + if (corners is None): + max_rad /= 2 + radius = min(max_rad, radius) + + # points remarquables + ptd_delta = radius * const_ptd_factor + (x_2, x_3) = (x_0 + radius, x_n - radius) + (x_1, x_4) = (x_2 - ptd_delta, x_3 + ptd_delta) + (y_2, y_3) = (y_0 + radius, y_n - radius) + (y_1, y_4) = (y_2 - ptd_delta, y_3 + ptd_delta) + + # liste des 4 points sommet du rectangle : angles sans raccord circulaire + angle_pts = ((x_0, y_0), (x_0, y_n), (x_n, y_n), (x_n, y_0)) + + # liste des 4 segments quadratique : raccord d'angle = radius + roundeds = [[(x_2, y_0), (x_1, y_0, 'c'), (x_0, y_1, 'c'), (x_0, y_2),], + [(x_0, y_3), (x_0, y_4, 'c'), (x_1, y_n, 'c'), (x_2, y_n),], + [(x_3, y_n), (x_4, y_n, 'c'), (x_n, y_4, 'c'), (x_n, y_3),], + [(x_n, y_2), (x_n, y_1, 'c'), (x_4, y_0, 'c'), (x_3, y_0),]] + + pts = [] + previous = None + for i in xrange(0, 4): + #BUGS ?? + if (corners[i]): + if (previous is not None) : + # on teste si non duplication de point + (nx, ny) = roundeds[i][0] + if (previous[0] == nx and previous[1] == ny) : + pts.pop() + pts.extend(roundeds[i]) + previous = roundeds[i][3] + else : + pts.append(angle_pts[i]) + + return pts + +def ellipse_coords(coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::ellipse_coords + # calcul des coords d'une ellipse + #--------------------------------------------------------------------------- + # paramètres : + # coords : coordonnées bbox du rectangle exinscrit + # options : + # -corners : liste des raccords de sommets + # [0 (aucun raccord)|1] par défaut [1,1,1,1] + #--------------------------------------------------------------------------- + """ + (x_0, y_0, x_n, y_n) = (coords[0][0], coords[0][1], + coords[1][0], coords[1][1]) + + if options.has_key('corners') : + corners = options.has_key('corners') + else: + corners = [1, 1, 1, 1] + + # attention aux formes 'négatives' + if (x_n < x_0) : + xs = x_0 + (x_0, x_n) = (x_n, xs) + if (y_n < y_0) : + ys = y_0 + (y_0, y_n) = (y_n, ys) + + # points remarquables + delta_x = (x_n - x_0)/2 * const_ptd_factor + delta_y = (y_n - y_0)/2 * const_ptd_factor + (x_2, y_2) = ((x_0+x_n)/2, (y_0+y_n)/2) + (x_1, x_3) = (x_2 - delta_x, x_2 + delta_x) + (y_1, y_3) = (y_2 - delta_y, y_2 + delta_y) + + # liste des 4 points sommet de l'ellipse : angles sans raccord circulaire + angle_pts = ((x_0, y_0), (x_0, y_n), (x_n, y_n), (x_n, y_0)) + + # liste des 4 segments quadratique : raccord d'angle = arc d'ellipse + roundeds = (((x_2, y_0), (x_1, y_0, 'c'), (x_0, y_1, 'c'), (x_0, y_2), ), + ((x_0, y_2), (x_0, y_3, 'c'), (x_1, y_n, 'c'), (x_2, y_n), ), + ((x_2, y_n), (x_3, y_n, 'c'), (x_n, y_3, 'c'), (x_n, y_2), ), + ((x_n, y_2), (x_n, y_1, 'c'), (x_3, y_0, 'c'), (x_2, y_0), )) + + pts = [] + previous = None + for i in xrange(0, 4): + if (corners[i]) : + if (previous) : + # on teste si non duplication de point + (nx, ny) = roundeds[i][0] + if (previous[0] == nx and previous[1] == ny) : + pts.pop() + + + pts.extend(roundeds[i]) + previous = roundeds[i][3] + + else : + pts.append(angle_pts[i]) + + return pts + + +def hippodrome_coords(coords, **options) : + """ + #--------------------------------------------------------------------------- + # Graphics::hippodrome_coords + # calcul des coords d'un hippodrome + #--------------------------------------------------------------------------- + # paramètres : + # coords : coordonnées bbox du rectangle exinscrit + # options : + # -orientation : orientation forcée de l'hippodrome [horizontal|vertical] + # -corners : liste des raccords de sommets [0|1] par défaut [1,1,1,1] + # -trunc : troncatures [left|right|top|bottom|both] + #--------------------------------------------------------------------------- + """ + (x_0, y_0, x_n, y_n) = (coords[0][0], + coords[0][1], + coords[1][0], + coords[1][1]) + + if (options.has_key('orientation')) : + orientation = options['orientation'] + else: + orientation = 'none' + + # orientation forcée de l'hippodrome + # (sinon hippodrome sur le plus petit coté) + if (orientation == 'horizontal') : + height = abs(y_n - y_0) + elif (orientation == 'vertical') : + height = abs(x_n - x_0) + else: + height = min(abs(x_n - x_0), abs(y_n - y_0)) + radius = height/2 + corners = (1, 1, 1, 1) + + if (options.has_key('corners')) : + corners = options['corners'] + + elif (options.has_key('trunc')) : + trunc = options['trunc'] + if (trunc == 'both') : + return ((x_0, y_0), (x_0, y_n), (x_n, y_n), (x_n, y_0)) + else : + if (trunc == 'left'): + corners = (0, 0, 1, 1) + elif (trunc == 'right'): + corners = (1, 1, 0, 0) + elif (trunc == 'top'): + corners = (0, 1, 1, 0) + elif (trunc == 'bottom') : + corners = (1, 0, 0, 1) + else : + corners = (1, 1, 1, 1) + + # l'hippodrome est un cas particulier de roundedRectangle + # on retourne en passant la 'configuration' à la fonction + # générique rounded_rectangle_coords + return rounded_rectangle_coords(coords, + radius = radius, + corners = corners) + +def polygon_coords(coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::polygon_coords + # calcul des coords d'un polygone régulier + #--------------------------------------------------------------------------- + # paramètres : + # coords : point centre du polygone + # options : + # -numsides : nombre de cotés + # -radius : rayon de définition du polygone + # (distance centre-sommets) + # -inner_radius : rayon interne (polygone type étoile) + # -corners : liste des raccords de sommets [0|1] + # par défaut [1,1,1,1] + # -corner_radius : rayon de raccord des cotés + # -startangle : angle de départ en degré du polygone + #--------------------------------------------------------------------------- + """ + if options.has_key('numsides'): + numsides = options['numsides'] + else : + numsides = 0 + if options.has_key('radius'): + radius = options['radius'] + else: + radius = None + if (numsides < 3 or not radius) : + raise ValueError("Vous devez au moins spécifier " + +"un nombre de cotés >= 3 et un rayon...\n") + + if (coords is None): + coords = (0, 0) + if (options.has_key('startangle')) : + startangle = options['startangle'] + else: + startangle = 0 + anglestep = 360/numsides + if options.has_key('inner_radius'): + inner_radius = options['inner_radius'] + else: + inner_radius = None + pts = [] + + # points du polygone + for i in xrange(0, numsides): + (xp, yp) = rad_point(coords, radius, startangle + (anglestep*i)) + pts.append((xp, yp)) + + # polygones 'étoiles' + if (inner_radius) : + (xp, yp) = rad_point(coords, inner_radius, + startangle + (anglestep*(i+ 0.5))) + pts.append((xp, yp)) + + pts.reverse() + + if (options.has_key('corner_radius')) : + if options.has_key('corners'): + corners = options['corners'] + else: + corners = None + return roundedcurve_coords(pts, + radius = options['corner_radius'], + corners = corners) + else : + return pts + + +def rounded_angle(widget, parentgroup, coords, radius) : + """ + #--------------------------------------------------------------------------- + # Graphics::rounded_angle + # THIS FUNCTION IS NO MORE USED, NEITHER EXPORTED + # curve d'angle avec raccord circulaire + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget Zinc + # parentgroup : identifiant de l'item group parent + # coords : les 3 points de l'angle + # radius : rayon de raccord + #--------------------------------------------------------------------------- + """ + (pt0, pt1, pt2) = coords + + (corner_pts, center_pts) = rounded_angle_coords(coords, radius) + (cx_0, cy_0) = center_pts + + if (parentgroup is None) : + parentgroup = 1 + + pts = [pt0] + pts.extend(corner_pts) + pts.append(pt2) + + widget.add('curve', + parentgroup, + lpts2coords(pts), + closed = 0, + linewidth = 1, + priority = 20, + ) + + +def rounded_angle_coords (coords, radius) : + """ + #--------------------------------------------------------------------------- + # Graphics::rounded_angle_coords + # calcul des coords d'un raccord d'angle circulaire + #--------------------------------------------------------------------------- + # le raccord circulaire de 2 droites sécantes est traditionnellement + # réalisé par un + # arc (conique) du cercle inscrit de rayon radius tangent à ces 2 droites + # + # Quadratique : + # une approche de cette courbe peut être réalisée simplement par le calcul + # de 4 points + # spécifiques qui définiront - quelle que soit la valeur de l'angle formé + # par les 2 + # droites - le segment de raccord : + # - les 2 points de tangence au cercle inscrit seront les points de début + # et de fin + # du segment de raccord + # - les 2 points de controle seront situés chacun sur le vecteur + # reliant le point de + # tangence au sommet de l'angle (point secant des 2 droites) + # leur position sur ce vecteur peut être simplifiée comme suit : + # - à un facteur de 0.5523 de la distance au sommet pour + # un angle >= 90° et <= 270° + # - à une 'réduction' de ce point vers le point de tangence + # pour les angles limites + # de 90° vers 0° et de 270° vers 360° + # ce facteur sera légérement modulé pour recouvrir plus précisement + # l'arc correspondant + #--------------------------------------------------------------------------- + # coords : les 3 points de l'angle + # radius : rayon de raccord + #--------------------------------------------------------------------------- + """ + (pt0, pt1, pt2) = coords + + # valeur d'angle et angle formé par la bisectrice + (angle, bisecangle) = vertex_angle(pt0, pt1, pt2) + + # distance au centre du cercle inscrit : rayon/sinus demi-angle + asin = sin(radians(angle/2)) + + if (asin) : + delta = abs(radius / asin) + else: + delta = radius + + # point centre du cercle inscrit de rayon $radius + if (angle < 180) : + refangle = bisecangle + 90 + else : + refangle = bisecangle - 90 + + (cx_0, cy_0) = rad_point(pt1, delta, refangle) + + # points de tangeance : pts perpendiculaires du centre aux 2 droites + (px_1, py_1) = perpendicular_point((cx_0, cy_0), (pt0, pt1)) + (px_2, py_2) = perpendicular_point((cx_0, cy_0), (pt1, pt2)) + + # point de controle de la quadratique + # facteur de positionnement sur le vecteur pt.tangence, sommet + ptd_factor = const_ptd_factor + if (angle < 90 or angle > 270) : + if (angle < 90) : + diffangle = angle + else: + diffangle = 360 - angle + if (diffangle > 15) : + ptd_factor -= (((90 - diffangle)/90) * (ptd_factor/4)) + ptd_factor = (diffangle/90) * (ptd_factor + + ((1 - ptd_factor) + * (90 - diffangle)/90)) + else : + diffangle = abs(180 - angle) + if (diffangle > 15) : + ptd_factor += (((90 - diffangle)/90) * (ptd_factor/3)) + + # delta xy aux pts de tangence + (d1x, d1y) = ((pt1[0] - px_1) * ptd_factor, (pt1[1] - py_1) * ptd_factor) + (d2x, d2y) = ((pt1[0] - px_2) * ptd_factor, (pt1[1] - py_2) * ptd_factor) + + # les 4 points de l'arc 'quadratique' + corner_pts = [(px_1, py_1), (px_1+d1x, py_1+d1y, 'c'), + (px_2+d2x, py_2+d2y, 'c'), (px_2, py_2)] + + + # retourne le segment de quadratique et le centre du cercle inscrit + return (corner_pts, (cx_0, cy_0)) + + +def roundedcurve_coords(coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::roundedcurve_coords + # retourne les coordonnées d'une curve à coins arrondis + #--------------------------------------------------------------------------- + # paramètres : + # coords : liste de coordonnées des points de la curve + # options : + # -radius : rayon de raccord d'angle + # -corners : liste des raccords de sommets [0|1] + # par défaut [1,1,1,1] + #--------------------------------------------------------------------------- + """ + numfaces = len(coords) + curve_pts = [] + + if (options.has_key('radius')) : + radius = options['radius'] + else: + radius = 0 + corners = None + if options.has_key('corners') : + corners = options['corners'] + + for index in xrange(numfaces): + if (corners is not None) : + if (index+1 > len(corners)) or not corners[index] : + curve_pts.append(coords[index]) + continue + + if (index) : + prev = index - 1 + else : + prev = numfaces - 1 + if (index > numfaces - 2) : + next = 0 + else : + next = index + 1 + anglecoords = (coords[prev], coords[index], coords[next]) + + quad_pts = rounded_angle_coords(anglecoords, radius)[0] + curve_pts.extend(quad_pts) + return curve_pts + + +def polyline_coords(coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::polyline_coords + # retourne les coordonnées d'une polyline + #--------------------------------------------------------------------------- + # paramètres : + # coords : liste de coordonnées des sommets de la polyline + # options : + # -radius : rayon global de raccord d'angle + # -corners : liste des raccords de sommets [0|1] + # par défaut [1,1,1,1], + # -corners_radius : liste des rayons de raccords de sommets + #--------------------------------------------------------------------------- + """ + numfaces = len(coords) + curve_pts = [] + + if (options.has_key('radius')) : + radius = options['radius'] + else: + radius = 0 + if options.has_key('corners_radius'): + corners_radius = options['corners_radius'] + corners = corners_radius + else: + corners_radius = None + if options.has_key('corners'): + corners = options['corners'] + else: + corners = None + + for index in xrange(0, numfaces): + if (corners is not None + and (len(corners) - 1 < index + or not corners[index])): + curve_pts.append(coords[index]) + else : + if (index) : + prev = index - 1 + else: + prev = numfaces - 1 + if (index > numfaces - 2) : + next = 0 + else: + next = index + 1 + anglecoords = (coords[prev], coords[index], coords[next]) + + if (corners_radius) : + rad = corners_radius[index] + else: + rad = radius + quad_pts = rounded_angle_coords(anglecoords, rad)[0] + curve_pts.extend(quad_pts) + + return curve_pts + +def pathline_coords (coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::pathline_coords + # retourne les coordonnées d'une pathline + #--------------------------------------------------------------------------- + # paramètres : + # coords : liste de coordonnées des points du path + # options : + # -closed : ligne fermée + # -shifting : sens de décalage du path (par défaut center) + # -linewidth : epaisseur de la ligne + #--------------------------------------------------------------------------- + """ + numfaces = len(coords) + pts = [] + + if options.has_key('closed'): + closed = options['closed'] + else: + closed = None + if (options.has_key('linewidth')) : + linewidth = options['linewidth'] + else: + linewidth = 2 + + if (options.has_key('shifting')) : + shifting = options['shifting'] + else: + shifting = 'center' + + if ( not numfaces or linewidth < 2): + raise ValueError("Invalid PathLine_coords") + + if (closed) : + previous = coords[numfaces - 1] + else: + previous = None + + next = coords[1] + if (shifting == 'center'): + linewidth /= 2 + + for i in xrange(0, numfaces): + pt = coords[i] + + if (previous is None) : + # extrémité de curve sans raccord -> angle plat + previous = (pt[0] + (pt[0] - next[0]), pt[1] + (pt[1] - next[1])) + + (angle, bisecangle) = vertex_angle(previous, pt, next) + + # distance au centre du cercle inscrit : rayon/sinus demi-angle + asin = sin(radians(angle/2)) + if (asin) : + delta = abs(linewidth / asin) + else: + delta = linewidth + + if (shifting == 'out' or shifting == 'in') : + if (shifting == 'out') : + adding = -90 + else: + adding = 90 + pts.append(rad_point(pt, delta, bisecangle + adding)) + pts.append(pt) + + else : + pts.append(rad_point(pt, delta, bisecangle-90)) + pts.append(rad_point(pt, delta, bisecangle+90)) + + if (i == numfaces - 2) : + if (closed) : + next = coords[0] + else: + next = (coords[i+1][0] + (coords[i+1][0] - pt[0]), + coords[i+1][1] + (coords[i+1][1] - pt[1])) + elif (i == numfaces - 1): + next = None + else : + next = coords[i+2] + + previous = coords[i] + + if (closed) : + pts.extend((pts[0], pts[1], pts[2], pts[3])) + + return pts + + +def curveline_coords(coords, **options) : + """ + #--------------------------------------------------------------------------- + # Graphics::curveline_coords + # retourne les coordonnées d'une curveLine + #--------------------------------------------------------------------------- + # paramètres : + # coords : liste de coordonnées des points de la ligne + # options : + # -closed : ligne fermée + # -shifting : sens de décalage du contour + # (par défaut center) + # -linewidth : epaisseur de la ligne + #--------------------------------------------------------------------------- + """ + numfaces = len(coords) + gopts = [] + backpts = [] + pts = [] + + if options.has_key('closed'): + closed = options['closed'] + else: + closed = None + if (options.has_key('linewidth')) : + linewidth = options['linewidth'] + else: + linewidth = 2 + if (options.has_key('shifting')) : + shifting = options['shifting'] + else: + shifting = 'center' + + if( not numfaces or linewidth < 2): + raise ValueError("Bad coords %s or linewidth %s"%(numfaces, linewidth)) + + if (closed) : + previous = coords[numfaces - 1] + else: + previous = None + + next = coords[1] + if (shifting == 'center'): + linewidth /= 2 + + for i in xrange(0, numfaces): + pt = coords[i] + + if ( previous is None ) : + # extrémité de curve sans raccord -> angle plat + previous = (pt[0] + (pt[0] - next[0]), pt[1] + (pt[1] - next[1])) + + + (angle, bisecangle) = vertex_angle(previous, pt, next) + + # distance au centre du cercle inscrit : rayon/sinus demi-angle + asin = sin(radians(angle/2)) + if (asin) : + delta = abs(linewidth / asin) + else: + delta = linewidth + + if (shifting == 'out' or shifting == 'in') : + if (shifting == 'out') : + adding = -90 + else: + adding = 90 + pts.append(rad_point(pt, delta, bisecangle + adding)) + pts.append(pt) + + else : + pts = rad_point(pt, delta, bisecangle+90) + gopts.append(pts) + pts = rad_point(pt, delta, bisecangle-90) + backpts.insert(0, pts) + + if (i == numfaces - 2) : + if (closed) : + next = coords[0] + else: + next = (coords[i+1][0] + + (coords[i+1][0] - pt[0]), coords[i+1][1] + + (coords[i+1][1] - pt[1])) + else : + next = coords[i+2] + + previous = coords[i] + + gopts.extend(backpts) + + if (closed) : + gopts.extend ((gopts[0], gopts[1])) + + return gopts + + +def shiftpath_coords(coords, **options) : + """ + #--------------------------------------------------------------------------- + # Graphics::shiftpath_coords + # retourne les coordonnées d'un décalage de path + #--------------------------------------------------------------------------- + # paramètres : + # coords : liste de coordonnées des points du path + # options : + # -closed : ligne fermée + # -shifting : <'out'|'in'> sens de décalage du path (par défaut out) + # -width : largeur de décalage (par défaut 1) + #--------------------------------------------------------------------------- + """ + numfaces = len(coords) + + if options.has_key('closed'): + closed = options['closed'] + else: + closed = None + if (options.has_key('width')) : + width = options['width'] + else: + width = 1 + if (options.has_key('shifting')) : + shifting = options['shifting'] + else: + shifting = 'out' + + if (not numfaces or not width): + return coords + + pts = [] + + if (closed) : + previous = coords[numfaces - 1] + else: + previous = None + next = coords[1] + + for i in xrange(0, numfaces): + pt = coords[i] + + if ( previous is None ) : + # extrémité de curve sans raccord -> angle plat + previous = (pt[0] + (pt[0] - next[0]), pt[1] + (pt[1] - next[1])) + + + (angle, bisecangle) = vertex_angle(previous, pt, next) + + # distance au centre du cercle inscrit : rayon/sinus demi-angle + asin = sin(radians(angle/2)) + if (asin) : + delta = abs(width / asin) + else: + delta = width + + if (shifting == 'out') : + adding = -90 + else: + adding = 90 + (x, y) = rad_point(pt, delta, bisecangle + adding) + pts.append((x, y)) + + + if (i > numfaces - 3) : + if (closed) : + next = coords[0] + else: + next = (pt[0] + (pt[0] - previous[0]), + pt[1] + (pt[1] - previous[1])) + + else : + next = coords[i+2] + + previous = coords[i] + + return pts + + + + +def curveitem2polyline_coords(widget, item, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::curveitem2polyline_coords + # Conversion des coordonnées Znitem curve (multicontours) + # en coordonnées polyline(s) + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # item : identifiant de l'item source + # options : + # -tunits : nombre pas de division des segments bezier + # (par défaut 20) + # -adjust : ajustement de la courbe de bezier (par défaut 1) + #--------------------------------------------------------------------------- + """ + if (not widget.type(item)): + raise ValueError("Item Not Found") + coords = [] + numcontours = widget.contour(item) + #parentgroup = widget.group(item) + + for contour in xrange(0, numcontours): + points = widget.coords(item, contour) + contourcoords = curve2polyline_coords(points, **options) + + coords.append(contourcoords) + + return coords + +def curve2polyline_coords(points, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::curve2polyline_coords + # Conversion curve -> polygone + #--------------------------------------------------------------------------- + # paramètres : + # points : liste des coordonnées curve à transformer + # options : + # -tunits : nombre pas de division des segments bezier + # (par défaut 20) + # -adjust : ajustement de la courbe de bezier (par défaut 1) + #--------------------------------------------------------------------------- + """ + if (options.has_key('tunits')) : + tunits = options['tunits'] + else: + tunits = 20 + if (options.has_key('adjust')) : + adjust = options['adjust'] + else: + adjust = 1 + + poly = [] + previous = None + bseg = [] + numseg = 0 + #prevtype = None + + for point in points: + if len(point) == 3: + (x, y, c) = point + elif len(point) == 2: + (x, y, c) = (point, None) + else: + ValueError("Bad point") + if (c == 'c') : + if not len(bseg) and previous: + bseg.append(previous) + bseg.append(point) + + else : + if (len (bseg)) : + bseg.append(point) + if (adjust) : + pts = bezier_compute(bseg, skipend = 1) + del pts[0] + del pts[0] + poly.extend(pts) + + else : + pts = bezier_segment(bseg, tunits = tunits, skipend = 1) + del pts[0] + del pts[0] + poly.extend(pts) + + bseg = [] + numseg += 1 + #prevtype = 'bseg' + + else : + poly.append((x, y)) + #prevtype = 'line' + + previous = point + + + return poly + + +def build_tabboxitem(widget, parentgroup, **options): + """ + #------------------------------------------------------------------------------- + # Graphics::build_tabboxitem + # construit les items de représentations Zinc d'une boite à onglets + #------------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # parentgroup : identifiant de l'item group parent + # + # options : + # -coords : coordonnées haut-gauche et bas-droite + # du rectangle + # englobant du Tabbox + # -params : arguments spécifiques des items curve + # à passer au widget + # -texture : ajout d'une texture aux items curve + # -tabtitles : table de hash de définition des titres onglets + # -pageitems : table de hash de définition des pages internes + # -relief : table de hash de définition du relief de forme + # + # (options de construction géometrique passées à tabbox_coords) + # -numpages : nombre de pages (onglets) de la boite + # -anchor : <'n'|'e'|'s'|'w'> ancrage (positionnement) de la ligne + # d'onglets + # -alignment : <'left'|'center'|'right'> alignement des onglets sur le coté + # d'ancrage + # -tabwidth : <'auto'>|| : largeur des onglets + # 'auto' largeur répartie, les largeurs sont auto-ajustée + # si besoin. + # -tabheight : <'auto'>| : hauteur des onglets + # -tabshift : <'auto'>| offset de 'biseau' entre base et haut de + # l'onglet (défaut auto) + # -radius : rayon des arrondis d'angle + # -overlap : <'auto'>| offset de recouvrement/séparation entre + # onglets + # -corners : liste 'spécifique' des raccords de sommets [0|1] + #--------------------------------------------------------------------------- + """ + if options.has_key('coords') : + coords = options['coords'] + else: + raise ValueError("Coords needed") + if options.has_key('params'): + params = options['params'] + else: + params = {} + if params.has_key('tags'): + tags = params['tags'] + else : + tags = [] + texture = None + + if (options.has_key('texture')) : + texture = get_texture(widget, + options['texture']) + + + if options.has_key('tabtitles'): + titlestyle = options['tabtitles'] + else : + titlestyle = None + if (titlestyle) : + titles = titlestyle['text'] + else: + titles = None + + tabs = [] + (shapes, tcoords, invert) = tabbox_coords(**options) + if (invert) : + k = len(shapes) + else: + k = -1 + shapes.reverse() + for shape in shapes : + if (invert) : + k -= 1 + else : + k += +1 + group = widget.add('group', parentgroup) + params['tags'] = tags + params['tags'] += (k, 'intercalaire') + form = widget.add('curve', + group, + lpts2coords(shape), + **params) + if texture : + widget.itemconfigure(form, tile = texture) + + if (options.has_key('relief')) : + graphicitem_relief(widget, form, **options['relief']) + + + if (options.has_key('page')) : + build_zinc_item(widget, group, **options['page']) + + if (titles) : + if (invert) : + tindex = k + else: + tindex = len(shapes) - k + titlestyle['itemtype'] = 'text' + titlestyle['coords'] = tcoords[tindex] + titlestyle['params']['text'] = titles[tindex] + ltags = list(tags) + ltags.append(tindex) + ltags.append('titre') + titlestyle['params']['tags'] = tuple(ltags) + build_zinc_item(widget, group, **titlestyle) + + return tabs + + +def tabbox_coords(coords, **options): + """ + #--------------------------------------------------------------------------- + # tabbox_coords + # Calcul des shapes de boites à onglets + #--------------------------------------------------------------------------- + # paramètres : + # coords : coordonnées haut-gauche bas-droite du rectangle + # englobant de la tabbox + # options + # -numpages : nombre de pages (onglets) de la boite + # -anchor : <'n'|'e'|'s'|'w'> ancrage (positionnement) de la + # ligne d'onglets + # -alignment : <'left'|'center'|'right'> alignement des onglets + # sur le coté d'ancrage + # -tabwidth : <'auto'>|| : largeur des onglets + # 'auto' largeur répartie, les largeurs sont auto-ajustée + # si besoin. + # -tabheight : <'auto'>| : hauteur des onglets + # -tabshift : <'auto'>| offset de 'biseau' entre base et haut + # de l'onglet (défaut auto) + # -radius : rayon des arrondis d'angle + # -overlap : <'auto'>| offset de recouvrement/séparation + # entre onglets + # -corners : liste 'spécifique' des raccords + # de sommets [0|1] + #--------------------------------------------------------------------------- + """ + (x_0, y_0) = coords[0] + (x_n, y_n) = coords[1] + shapes, titles_coords = [], [] + inverse = None + + #loptions = options.keys() + if options.has_key('numpages'): + numpages = options['numpages'] + else: + numpages = 0 + + if (not x_0 or not y_0 or not x_n or not y_n or not numpages) : + raise ValueError("Vous devez au minimum spécifier\ + le rectangle englobant et le nombre de pages") + + if (options.has_key('anchor')) : + anchor = options['anchor'] + else: + anchor = 'n' + if (options.has_key('alignment')) : + alignment = options['alignment'] + else: + alignment ='left' + + + if (options.has_key('tabwidth')) : + nlen = options['tabwidth'] + else: + nlen ='auto' + if (options.has_key('tabheight')) : + thick = options['tabheight'] + else: + thick ='auto' + if (options.has_key('tabshift')) : + biso = options['tabshift'] + else: + biso = 'auto' + if (options.has_key('radius')) : + radius = options['radius'] + else: + radius = 0 + if (options.has_key('overlap')) : + overlap = options['overlap'] + else: + overlap = 0 + if (options.has_key('corners')): + corners = options['corners'] + else: + corners = None + if (anchor in ( 'n', 's')) : + orientation = 'horizontal' + else: + orientation = 'vertical' + if (orientation == 'horizontal') : + maxwidth = (x_n - x_0) + else: + maxwidth = (y_n - y_0) + tabswidth = 0 + align = 1 + + if (nlen == 'auto') : + tabswidth = maxwidth + nlen = float(tabswidth + (overlap * (numpages - 1)))/numpages + else : + if (type(nlen) in (types.TupleType, types.ListType )) : + for w in nlen : + tabswidth += (w - overlap) + + tabswidth += overlap + else : + tabswidth = (nlen * numpages) - (overlap * (numpages - 1)) + + + if (tabswidth > maxwidth) : + tabswidth = maxwidth + nlen = float(tabswidth + (overlap * (numpages - 1)))/numpages + + if (alignment == 'center' and ((maxwidth - tabswidth) > radius)): + align = 0 + + + if (thick == 'auto') : + if (orientation == 'horizontal') : + thick = int((y_n - y_0)/10) + else: + thick = int((x_n - y_0)/10) + thick = max(10, thick) + thick = min(40, thick) + + if (biso == 'auto') : + biso = int(thick/2) + + if ((alignment == 'right' and anchor != 'w') or + (anchor == 'w' and alignment != 'right')) : + + if (type(nlen) in (types.TupleType, types.ListType)) : + for p in xrange(0, numpages): + nlen[p] *= -1 + else : + nlen *= -1 + biso *= -1 + overlap *= -1 + + if (alignment == 'center') : + (biso1, biso2) = (biso/2, biso/2) + else: + (biso1, biso2) = (0, biso) + + cadre, tabdxy = [], [] + xref, yref = 0, 0 + if (orientation == 'vertical') : + if (anchor == 'w'): + thick *= -1 + if (anchor == 'w') : + (startx, endx) = (x_0, x_n) + else: + (startx, endx) = (x_n, x_0) + if ((anchor == 'w' and alignment != 'right') or + (anchor == 'e' and alignment == 'right')) : + (starty, endy) = (y_n, y_0) + else: + (starty, endy) = (y_0, y_n) + + xref = startx - thick + yref = starty + if (alignment == 'center') : + if (anchor == 'w') : + ratio = -2 + else: + ratio = 2 + yref += (float(maxwidth - tabswidth)/ratio) + + cadre = ((xref, endy), (endx, endy), (endx, starty), (xref, starty)) + + # flag de retournement de la liste des pts de curve si nécessaire + # -> sens anti-horaire + inverse = (alignment == 'right') + + else : + if (anchor == 's'): + thick *= -1 + (starty, endy) = (y_n, y_0) + else : + (starty, endy) = (y_0, y_n) + + if (alignment == 'right') : + (startx, endx) = (x_n, x_0) + else: + (startx, endx) = (x_0, x_n) + + + yref = starty + thick + if (alignment == 'center') : + xref = x_0 + (float(maxwidth - tabswidth)/2) + else : + xref = startx + + cadre = ((endx, yref), (endx, endy), (startx, endy), (startx, yref)) + + # flag de retournement de la liste des pts de curve si nécessaire + # -> sens anti-horaire + inverse = ((anchor == 'n' and alignment != 'right') + or (anchor == 's' and alignment == 'right')) + + + for i in xrange(0, numpages): + pts = [] + + # décrochage onglet + #push (pts, ([xref, yref])) if i > 0 + + # cadre + pts.extend(cadre) + + # points onglets + if (i > 0 or not align) : + pts.append((xref, yref)) + + if (type(nlen) in (types.TupleType, types.ListType)) : + tw = nlen[i] + else: + tw = nlen + + if (type(nlen) in (types.TupleType, types.ListType)) : + slen = len(nlen) + else: + slen = nlen + + if (orientation == 'vertical') : + tabdxy = ((thick, biso1), (thick, tw - biso2), (0, tw)) + else: + tabdxy = ((biso1, -thick), (tw - biso2, -thick), (tw, 0)) + for delta_xy in tabdxy : + pts.append((xref + delta_xy[0], yref + delta_xy[1])) + + + if (radius) : + if (not options.has_key('corners')) : + if (i > 0 or not align) : + corners = (0, 1, 1, 0, 0, 1, 1, 0) + else : + corners = (0, 1, 1, 0, 1, 1, 0, 0, 0) + + curvepts = roundedcurve_coords(pts, + radius = radius, + corners = corners) + lcurvepts = list(curvepts) + if (inverse): + lcurvepts.reverse() + shapes.append(lcurvepts) + else : + if (inverse): + pts.reverse() + shapes.append(pts) + + if (orientation == 'horizontal') : + titles_coords.append((float(xref) + (tw - (biso2 - biso1))/2, + float(yref) - (thick/2))) + xref += (tw - overlap) + + else : + titles_coords.append( (float(xref) + (thick/2), + yref + (slen - ((biso2 - biso1)/2))/2)) + yref += (slen - overlap) + + return (shapes, titles_coords, inverse) + + +def graphicitem_relief(widget, item, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::graphicitem_relief + # construit un relief à l'item Zinc en utilisant des items Triangles + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # item : identifiant de l'item zinc + # options : table d'options + # -closed : le relief assure la fermeture de forme (défaut 1) + # -profil : <'rounded'|'flat'> type de profil (defaut 'rounded') + # -relief : <'raised'|'sunken'> (défaut 'raised') + # -side : <'inside'|'outside'> relief interne ou externe à la forme + # (défaut 'inside') + # -color : couleur du relief (défaut couleur de la forme) + # -smoothed : facettes relief lissées ou non (défaut 1) + # -lightangle : angle d'éclairage (défaut valeur générale widget) + # -width : 'épaisseur' du relief en pixel + # -fine : mode précision courbe de bezier + # (défaut 0 : auto-ajustée) + #------------------------------------------------------------------------------- + """ + items = [] + + # relief d'une liste d'items -> appel récursif + if (type(item) in (types.TupleType, types.ListType)) : + for part in item : + items.extend(graphicitem_relief(widget, part, **options)) + else : + itemtype = widget.type(item) + if not itemtype: + raise ValueError("Bad Item") + + parentgroup = widget.group(item) + if (options.has_key('priority')) : + priority = options['priority'] + else : + priority = widget.itemcget(item, 'priority')+1 + + # coords transformés (polyline) de l'item + adjust = not options['fine'] + for coords in zincitem_2_curvecoords(widget, + item, linear = 1, + realcoords = 1, + adjust = adjust) : + (pts, colors) = polyline_relief_params(widget, + item, + coords, + **options) + + items.append(widget.add('triangles', + parentgroup, + pts, + priority = priority, + colors = colors)) + + + # renforcement du contour + if (widget.itemcget(item, 'linewidth')) : + items.append(widget.clone(item, + filled = 0, + priority = priority+1)) + + return items + + +def polyline_relief_params(widget, item, coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::polyline_relief_params + # retourne la liste des points et des couleurs nécessaires à la construction + # de l'item Triangles du relief + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant widget Zinc + # item : identifiant item Zinc + # options : table d'options + # -closed : le relief assure la fermeture de forme (défaut 1) + # -profil : <'rounded'|'flat'> type de profil (defaut 'rounded') + # -relief : <'raised'|'sunken'> (défaut 'raised') + # -side : <'inside'|'outside'> relief interne ou externe à la forme + # (défaut 'inside') + # -color : couleur du relief (défaut couleur de la forme) + # -smoothed : facettes relief lissées ou non (défaut 1) + # -lightangle : angle d'éclairage (défaut valeur générale widget) + # -width : 'épaisseur' du relief en pixel + #--------------------------------------------------------------------------- + """ + + if (options.has_key('closed')) : + closed = options['closed'] + else: + closed = 1 + if (options.has_key('profil')) : + profil = options['profil'] + else: + profil = 'rounded' + if (options.has_key('relief')) : + relief = options['relief'] + else : + relief = 'raised' + if (options.has_key('side')) : + side = options['side'] + else: + side = 'inside' + if (options.has_key('color')) : + basiccolor = options['color'] + else: + basiccolor = zincitem_predominantcolor(widget, item) + if (options.has_key('smooth')) : + smoothed = options['smooth'] + else: + smoothed = 1 + if (options.has_key('lightangle')) : + lightangle = options['lightangle'] + else: + lightangle = widget.cget('lightangle') + + if options.has_key('width'): + width = options['width'] + else: + raise ValueError('Options must have width field') + if ( width < 1) : + (x_0, y_0, x_1, y_1) = widget.bbox(item) + width = min(x_1 -x_0, y_1 - y_0)/10 + if (width < 2) : + width = 2 + + numfaces = len(coords) + if (closed): + previous = coords[numfaces - 1] + else: + previous = None + next = coords[1] + + pts = [] + colors = [] + alpha = 100 + m = re.compile("^(?P#[0-9a-fA-F]{6});(?P\d{1,2})$") + res = m.match(basiccolor) + if (res is not None) : + (basiccolor, alpha) = res.group('color'), res.group('alpha') + + if ( options.has_key('color')): + color = options['color'] + res = m.match(color) + if ((res is None) and (profil == 'flat')): + alpha /= 2 + + if (profil == 'rounded') : + reliefalphas = [0, alpha] + else: + reliefalphas = [alpha, alpha] + + for i in xrange(0, numfaces): + pt = coords[i] + + if (previous) : + # extrémité de curve sans raccord -> angle plat + previous = (pt[0] + (pt[0] - next[0]), pt[1] + (pt[1] - next[1])) + + + (angle, bisecangle) = vertex_angle(previous, pt, next) + + # distance au centre du cercle inscrit : rayon/sinus demi-angle + asin = sin(radians(angle/2)) + if (asin) : + delta = abs(width / asin) + else: + delta = width + if (side == 'outside') : + decal = -90 + else: + decal = 90 + + shift_pt = rad_point(pt, delta, bisecangle+decal) + pts.append(shift_pt) + pts.append(pt) + + if (smoothed and i) : + pts.append(shift_pt) + pts.append(pt) + + + faceangle = 360 -(linenormal(previous, next)+90) + + light = abs(lightangle - faceangle) + if (light > 180): + light = 360 - light + if light < 1: + light = 1 + + if (relief == 'sunken') : + lumratio = (180-light)/180 + else: + lumratio = light/180 + + if ( not smoothed and i) : + #A VOIR + #OBSCURE + colors.extend((colors[-2], colors[-1])) + + if (basiccolor) : + # création des couleurs dérivées + shade = lightingcolor(basiccolor, lumratio) + color0 = "%s;%s"% (shade, reliefalphas[0]) + color1 = "%s;%s"% (shade, reliefalphas[1]) + colors.extend((color0, color1)) + + else : + c = (255*lumratio) + color0 = hexargbcolor(c, c, c, reliefalphas[0]) + color1 = hexargbcolor(c, c, c, reliefalphas[1]) + colors.extend((color0, color1)) + + + if (i == (numfaces - 2)) : + if (closed) : + next = coords[0] + else: + next = (coords[i+1][0] + (coords[i+1][0] - pt[0]), + coords[i+1][1] + (coords[i+1][1] - pt[1])) + else : + next = coords[i+2] + + previous = coords[i] + + if (closed) : + pts.extend((pts[0], pts[1], pts[2], pts[3])) + colors.extend((colors[0], colors[1])) + + if (not smoothed) : + pts.extend((pts[0], pts[1], pts[2], pts[3])) + colors.extend((colors[0], colors[1])) + + return (pts, colors) + + +def graphicitem_shadow(widget, item, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::graphicitem_shadow + # Création d'une ombre portée à l'item + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant widget Zinc + # item : identifiant item Zinc + # options : table d'options + # -opacity : opacité de l'ombre (défaut 50) + # -filled : remplissage totale de l'ombre (hors bordure) (defaut 1) + # -lightangle : angle d'éclairage (défaut valeur générale widget) + # -distance : distance de projection de l'ombre en pixel + # -enlarging : grossi de l'ombre portée en pixels (defaut 0) + # -width : taille de diffusion/diffraction (défaut 4) + # -color : couleur de l'ombre portée (défaut black) + #--------------------------------------------------------------------------- + """ + items = [] + + # relief d'une liste d'items -> appel récursif + if (type(item) in (types.TupleType, types.ListType)) : + for part in item : + items.append(graphicitem_shadow(widget, part, **options)) + return items + + else : + + itemtype = widget.type(item) + + if not itemtype : + raise ValueError("Not a valid Item Id %s"%item) + + # création d'un groupe à l'ombre portée + if (options.has_key('parentgroup')) : + parentgroup = options['parentgroup'] + else: + parentgroup = widget.group(item) + if (options.has_key('priority')) : + priority = options['priority'] + else: + priority = widget.itemcget(item, 'priority')-1 + priority = max(0, priority) + + shadow = widget.add('group', parentgroup, priority = priority) + + if (itemtype == 'text') : + if (options.has_key('opacity')) : + opacity = options['opacity'] + else: + opacity = 50 + if (options['color']) : + color = options['color'] + else: + color = '#000000' + + clone = widget.clone(item, color = "%s;%s"% (color, opacity)) + widget.chggroup(clone, shadow) + + else : + + # création des items (de dessin) de l'ombre + if ( options.has_key('filled')) : + filled = options['filled'] + else: + filled = 1 + + # coords transformés (polyline) de l'item + for coords in zincitem_2_curvecoords(widget, + item, + linear = 1, + realcoords = 1) : + (t_pts, i_pts, colors) = polyline_shadow_params( coords, + **options) + + # option filled : remplissage hors bordure + # de l'ombre portée (item curve) + if (filled) : + if (len(items)) : + widget.contour(items[0], 'add', 0, i_pts) + + else : + items.append( widget.add('curve', shadow, i_pts, + linewidth = 0, + filled = 1, + fillcolor = colors[0], + )) + + # bordure de diffusion de l'ombre (item triangles) + items.append( widget.add('triangles', shadow, t_pts, + colors = colors)) + + + # positionnement de l'ombre portée + if (options.has_key('distance')) : + distance = options['distance'] + else: + distance = 10 + if (options.has_key('lightangle')) : + lightangle = options['lightangle'] + else: + lightangle = widget.cget('lightangle') + + (delta_x, delta_y) = rad_point((0, 0), + distance, + lightangle+180) + widget.translate(shadow, delta_x, -delta_y) + + return shadow + + +def polyline_shadow_params(coords, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::polyline_shadow_params + # retourne les listes des points et de couleurs nécessaires à la + # construction des items triangles (bordure externe) et curve + # (remplissage interne) de l'ombre portée + #--------------------------------------------------------------------------- + # paramètres : + # coords : coordonnées + # options : table d'options + # -opacity : opacité de l'ombre (défaut 50) + # -lightangle : angle d'éclairage (défaut valeur générale widget) + # -distance : distance de projection de l'ombre en pixel + # (défaut 10) + # -enlarging : grossi de l'ombre portée en pixels (defaut 2) + # -width : taille de diffusion/diffraction + # (défaut distance -2) + # -color : couleur de l'ombre portée (défaut black) + #--------------------------------------------------------------------------- + """ + if (options.has_key('distance')) : + distance = options['distance'] + else: + distance = 10 + if (options.has_key('width')) : + width = options['width'] + else: + width = distance-2 + if (options.has_key('opacity')) : + opacity = options['opacity'] + else: + opacity = 50 + if (options.has_key('color')) : + color = options['color'] + else: + color ='#000000' + if (options.has_key('enlarging')) : + enlarging = options['enlarging'] + else : + enlarging = 2 + + if (enlarging) : + coords = shiftpath_coords(coords, + width = enlarging, + closed = 1, + shifting = 'out') + + numfaces = len(coords) + previous = coords[numfaces - 1] + next = coords[1] + + t_pts = [] + i_pts = [] + colors = [] + (color0, color1) = ("%s;%s"% (color, opacity), "%s;0"% color) + + for i in xrange(0, numfaces): + pt = coords[i] + + #A VOIR + #Je ne vois pas quand cela peut arriver + if (not previous) : + # extrémité de curve sans raccord -> angle plat + previous = (pt[0] + (pt[0] - next[0]), pt[1] + (pt[1] - next[1])) + + (angle, bisecangle) = vertex_angle(previous, pt, next) + + # distance au centre du cercle inscrit : rayon/sinus demi-angle + asin = sin(radians(angle/2)) + if (asin) : + delta = abs(width / asin) + else : + delta = width + decal = 90 + + shift_pt = rad_point(pt, delta, bisecangle+decal) + i_pts.append(shift_pt) + t_pts.append(shift_pt) + t_pts.append(pt) + + colors.append(color0) + colors.append(color1) + if (i == numfaces - 2) : + next = coords[0] + else : + next = coords[i+2] + + previous = coords[i] + + # fermeture + t_pts.extend((t_pts[0], t_pts[1], t_pts[2], t_pts[3])) + i_pts.extend((t_pts[0], t_pts[1])) + colors.extend((color0, color1, color0, color1)) + + return (t_pts, i_pts, colors) + + + + + +#Local Variables: +#mode : python +#tab-width: 4 +#end: diff --git a/Python/library/pictorial.py b/Python/library/pictorial.py new file mode 100644 index 0000000..4c0546b --- /dev/null +++ b/Python/library/pictorial.py @@ -0,0 +1,766 @@ +# -*- coding: iso-8859-1 -*- +# Pictorial Functions : +# ---------------------- +# set_gradients +# get_pattern +# get_texture +# get_image +# init_pixmaps +# zincitem_predominantcolor +# zncolor_to_rgb +# hexargbcolor +# create_graduate +# path_graduate +# mediancolor +# lightingcolor +# rgb_to_lch +# lch_to_rgb +# rgb_to_hls +# hls_to_rgb + +import PIL.Image, PIL.ImageTk +import re +from math import pi, radians, atan2, sqrt, sin, cos + +# initialisation et partage de ressources couleurs et images +textures = {} +IMAGES = {} +bitmaps = {} +AVERAGE_COLOR = '#777777' + + +_GRADIENTS = [] + +# constante white point (conversion couleur espace CIE XYZ) +(Xw, Yw, Zw) = (95.047, 100.0, 108.883) + +def set_gradients(zinc, **grads): + """ + #--------------------------------------------------------------------------- + # Graphics::set_gradients + # création de gradient nommés Zinc + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # **grads : de définition de couleurs zinc + #--------------------------------------------------------------------------- + """ + global _GRADIENTS + if (not _GRADIENTS): + _GRADIENTS = [] + for (name, gradient) in grads.items(): + zinc.gname(gradient, name) + _GRADIENTS.append(name) + +def rgb_dec2hex(rgb): + """ + #--------------------------------------------------------------------------- + # Graphics::rgb_dec2hex + # conversion d'une couleur RGB (255,255,255) au format Zinc '#ffffff' + #--------------------------------------------------------------------------- + # paramètres : + # rgb : liste de couleurs au format RGB + #--------------------------------------------------------------------------- + """ + return "#%04x%04x%04x"% rgb + +def path_graduate(zinc, numcolors, style): + """ + #--------------------------------------------------------------------------- + # Graphics::path_graduate + # création d'un jeu de couleurs dégradées pour item pathline + #--------------------------------------------------------------------------- + """ + typ = style['type'] + if (typ == 'linear'): + return create_graduate(numcolors, style['colors'], 2) + elif (typ == 'double'): + colors1 = create_graduate(numcolors/2+1, + style['colors'][0]) + colors2 = create_graduate(numcolors/2+1, + style['colors'][1]) + colors = [] + for i in xrange(numcolors+1): + colors.extend([colors1[i], colors2[i]]) + return colors + elif (typ == 'transversal'): + (c1, c2) = style['colors'] + colors = [c1, c2] + for i in xrange(numcolors): + colors.extend([c1, c2]) + + return colors + +def create_graduate(totalsteps, refcolors, repeat = 1): + """ + #--------------------------------------------------------------------------- + # Graphics::create_graduate + # création d'un jeu de couleurs intermédiaires (dégradé) entre n couleurs + #--------------------------------------------------------------------------- + """ + colors = [] + + numgraduates = len(refcolors) - 1 + if (numgraduates < 1): + raise ValueError("Le degradé necessite\ + au moins 2 couleurs de référence...") + steps = None + if (numgraduates > 1): + steps = totalsteps/(numgraduates - 1) + else: + steps = totalsteps + + for c in xrange(numgraduates): + (c1, c2) = (refcolors[c], refcolors[c + 1]) + + for i in xrange(steps): + color = mediancolor(c1, c2, i / (steps - 1)) + for it in xrange(repeat): + colors.append(color) + + if (c < numgraduates - 1): + for k in xrange(repeat): + colors.pop() + + return colors + +def lightingcolor (color, new_l) : + """ + #--------------------------------------------------------------------------- + # Graphics::lightingcolor + # modification d'une couleur par sa composante luminosité + #--------------------------------------------------------------------------- + # paramètres : + # color : couleur au format zinc + # new_l : (de 0 à 1) nouvelle valeur de luminosité + #--------------------------------------------------------------------------- + """ + (h, l, s) = 0, 0, 0 + rgb = hexa2rgb(color) + h, l, s = rgb_to_hls(rgb) + new_l = min(new_l, 1) + (n_r, n_g, n_b) = hls_to_rgb(h, new_l, s) + return hexargbcolor(n_r*255, n_g*255, n_b*255) + + +def get_predominantcolor(colors): + """ + #--------------------------------------------------------------------------- + # Graphics::get_predominantcolor + # donne la couleur dominante + #--------------------------------------------------------------------------- + # paramètres : + # colors : * liste de couleurs au format zinc + #--------------------------------------------------------------------------- + """ + (rs, gs, bs, as, numcolors) = (0, 0, 0, 0, 0) + for color in colors : + (r, g, b, a) = zncolor_to_rgb(color) + rs += r + gs += g + bs += b + as += a + numcolors += 1 + + new_r = int(rs/numcolors) + new_g = int(gs/numcolors) + new_b = int(bs/numcolors) + new_a = int(as/numcolors) + + newcolor = hexargbcolor(new_r, new_g, new_b, new_a) + return newcolor + +def zincitem_predominantcolor(widget, item): + """ + #--------------------------------------------------------------------------- + # Graphics::zincitem_predominantcolor + # retourne la couleur dominante d'un item ('barycentre' gradiant fillcolor) + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # item : identifiant de l'item zinc + #--------------------------------------------------------------------------- + """ + typ = widget.type(item) + if not typ : + raise ValueError("Not a Valid Item %s" % item) + if (typ == 'text' or typ == 'icon') : + return widget.itemcget(item, 'color') + + elif (typ == 'triangles' or + typ == 'rectangle' or + typ == 'arc' or + typ == 'curve') : + + colors = [] + + if (typ == 'triangles') : + colors = widget.itemcget(item, 'colors') + else : + grad = widget.itemcget(item, 'fillcolor') + regexp = re.compile( + "^=(?P\w+)(?P[^|]+)\|(?P.*)$") + res = regexp.match(grad) + if (res is None): + #couleur simple + return grad + else: + #Gradient + colorspart = res.group('colorparts').split("|") + regexp_color = re.compile("^(?P^\S+).*") + for colorpart in colorspart: + res = regexp_color.match(colorpart) + if res: + colors.append(res.group('color')) + else : + raise ValueError("Impossible case!!") + return get_predominantcolor(colors) + else : + return AVERAGE_COLOR + +def mediancolor (color1, color2, rate) : + """ + #--------------------------------------------------------------------------- + # Graphics::mediancolor + # calcul d'une couleur intermédiaire défini par un ratio ($rate) + # entre 2 couleurs + #--------------------------------------------------------------------------- + # paramètres : + # color1 : première couleur zinc + # color2 : seconde couleur zinc + # rate : (de 0 à 1) position de la couleur intermédiaire + #--------------------------------------------------------------------------- + """ + if (rate > 1): + rate = 1 + if (rate < 0): + rate = 0 + + (r0, g0, b0, a0) = zncolor_to_rgb(color1) + (r1, g1, b1, a1) = zncolor_to_rgb(color2) + + r = r0 + int((r1 - r0) * rate) + g = g0 + int((g1 - g0) * rate) + b = b0 + int((b1 - b0) * rate) + a = a0 + int((a1 - a0) * rate) + + return hexargbcolor(r, g, b, a) + + +def zncolor_to_rgb (zncolor): + """ + #--------------------------------------------------------------------------- + # Graphics::zncolor_to_rgb + # conversion d'une couleur Zinc au format RGBA (255,255,255,100) + #--------------------------------------------------------------------------- + # paramètres : + # zncolor : couleur au format hexa zinc (#ffffff ou #ffffffffffff) + #--------------------------------------------------------------------------- + """ + #Recherche du format d'entrée + # ffffff ou ffffffffffff avec ou sans alpha + #test présence alpha + res = [] + res.append( + re.match( + "^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2});(?P\d{1,3})$" + ,zncolor)) + res.append( + re.match( + "^#([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4});(?P\d{1,3})$" + ,zncolor)) + #Pas de alpha + res.append(re.match("^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$" + ,zncolor)) + res.append(re.match("^#([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})$" + ,zncolor)) + res.sort() + resultat = res.pop() + if res is None: + raise ValueError("Not a valid zinc color") + alpha = 100 + res = resultat.groupdict() + if res.has_key('alpha'): + alpha = int(res['alpha']) + else: + alpha = 100 + + R = int(resultat.group(1), 16) + G = int(resultat.group(2), 16) + B = int(resultat.group(3), 16) + + return (R, G, B, alpha) + +def rgb_to_lch(r, g, b) : + """ + #--------------------------------------------------------------------------- + # ALGORYTHMES DE CONVERSION ENTRE ESPACES DE COULEURS + #--------------------------------------------------------------------------- + #--------------------------------------------------------------------------- + # Graphics::rgb_to_lch + # Algorythme de conversion RGB -> CIE LCH° + #--------------------------------------------------------------------------- + # paramètres : + # r : (de 0 à 1) valeur de la composante rouge de la couleur RGB + # g : (de 0 à 1) valeur de la composante verte de la couleur RGB + # b : (de 0 à 1) valeur de la composante bleue de la couleur RGB + #--------------------------------------------------------------------------- + """ + # Conversion RGBtoXYZ + gamma = 2.4 + rgblimit = 0.03928 + + if (r > rgblimit): + r = ((r + 0.055)/1.055)**gamma + else : + r = r / 12.92 + + if (g > rgblimit) : + g = ((g + 0.055)/1.055)**gamma + else: + g = g / 12.92 + + if (b > rgblimit) : + b = ((b + 0.055)/1.055)**gamma + else: + b = b / 12.92 + + r *= 100 + g *= 100 + b *= 100 + + X = (0.4124 * r) + (0.3576 * g) + (0.1805 * b) + Y = (0.2126 * r) + (0.7152 * g) + (0.0722 * b) + Z = (0.0193 * r) + (0.1192 * g) + (0.9505 * b) + + # Conversion XYZtoLab + gamma = 1/3 + (L, A, B) = 0, 0, 0 + + if (Y == 0) : + (L, A, B) = (0, 0, 0) + else : + #Utilisation des constantes white point (variables globale) + (Xs, Ys, Zs) = (X/Xw, Y/Yw, Z/Zw) + + + if (Xs > 0.008856) : + Xs = Xs**gamma + else : + Xs = (7.787 * Xs) + (16/116) + + if (Ys > 0.008856) : + Ys = Ys**gamma + else : + Ys = (7.787 * Ys) + (16/116) + + if (Zs > 0.008856) : + Zs = Zs**gamma + else : + Zs = (7.787 * Zs) + (16/116) + + L = (116.0 * Ys) - 16.0 + + A = 500 * (Xs - Ys) + B = 200 * (Ys - Zs) + + # conversion LabtoLCH + (C, H) = 0, 0 + + + if (A == 0) : + H = 0 + else : + H = atan2(B, A) + + if (H > 0) : + H = (H / pi) * 180 + + else : + H = 360 - ( abs(H) / pi) * 180 + + + + C = sqrt(A**2 + B**2) + + return (L, C, H) + +def lch_to_rgb (L, C, H) : + """ + #--------------------------------------------------------------------------- + # Graphics::lch_to_rgb + # Algorythme de conversion CIE L*CH -> RGB + #--------------------------------------------------------------------------- + # paramètres : + # L : (de 0 à 1) valeur de la composante luminosité + # de la couleur CIE LCH + # C : (de 0 à 1) valeur de la composante saturation + # de la couleur CIE LCH + # H : (de 0 à 1) valeur de la composante teinte + # de la couleur CIE LCH + #--------------------------------------------------------------------------- + """ + (a, b) = 0, 0 + + # Conversion LCHtoLab + a = cos( radians(H)) * C + b = sin( radians(H)) * C + + # Conversion LabtoXYZ + gamma = 3 + (X, Y, Z) = 0, 0, 0 + + Ys = (L + 16.0) / 116.0 + Xs = (a / 500) + Ys + Zs = Ys - (b / 200) + + if ((Ys**gamma) > 0.008856) : + Ys = Ys**gamma + else : + Ys = (Ys - 16 / 116) / 7.787 + + if ((Xs**gamma) > 0.008856) : + Xs = Xs**gamma + else : + Xs = (Xs - 16 / 116) / 7.787 + + if ((Zs**gamma) > 0.008856) : + Zs = Zs**gamma + else : + Zs = (Zs - 16 / 116) / 7.787 + + + X = Xw * Xs + Y = Yw * Ys + Z = Zw * Zs + + # Conversion XYZtoRGB + gamma = 1/2.4 + rgblimit = 0.00304 + (R, G, B) = (0, 0, 0) + + X /= 100 + Y /= 100 + Z /= 100 + + R = (3.2410 * X) + (-1.5374 * Y) + (-0.4986 * Z) + G = (-0.9692 * X) + (1.8760 * Y) + (0.0416 * Z) + B = (0.0556 * X) + (-0.2040 * Y) + (1.0570 * Z) + + if (R > rgblimit) : + R = (1.055 * (R**gamma)) - 0.055 + else : + R = (12.92 * R) + + if (G > rgblimit) : + G = (1.055 * (G**gamma)) - 0.055 + else : + G = (12.92 * G) + + if (B > rgblimit) : + B = (1.055 * (B**gamma)) - 0.055 + else : + B = (12.92 * B) + + if (R < 0) : + R = 0 + elif (R > 1.0) : + R = 1.0 + else : + R = _trunc(R, 5) + + if (G < 0) : + G = 0 + elif (G > 1.0) : + G = 1.0 + else : + G = _trunc(G, 5) + + if (B < 0) : + B = 0 + elif (B > 1.0) : + B = 1.0 + else : + B = _trunc(B, 5) + + return (R, G, B) + +def rgb_to_hls(r, g, b): + """ + #--------------------------------------------------------------------------- + # Graphics::rgb_to_hls + # Algorythme de conversion RGB -> HLS + #--------------------------------------------------------------------------- + # r : (de 0 à 1) valeur de la composante rouge de la couleur RGB + # g : (de 0 à 1) valeur de la composante verte de la couleur RGB + # b : (de 0 à 1) valeur de la composante bleue de la couleur RGB + #--------------------------------------------------------------------------- + """ + H, L, S = 0, 0, 0 + minv, maxv, diffv = 0, 0, 0 + maxv = max(r, g, b) + minv = min(r, g, b) + + # calcul de la luminosité + L = (maxv + minv) / 2 + + # calcul de la saturation + if (maxv == minv) : + # couleur a-chromatique (gris) r = g = b + S = 0 + H = None + return [H, L, S] + + # couleurs "Chromatiques" -------------------- + + # calcul de la saturation + if (L <= 0.5) : + S = (maxv - minv) / (maxv + minv) + + else : + S = (maxv - minv) / (2 - maxv - minv) + + # calcul de la teinte + diffv = maxv - minv + + if (r == maxv) : + # couleur entre jaune et magenta + H = (g - b) / diffv + + elif (g == maxv) : + # couleur entre cyan et jaune + H = 2 + (b - r) / diffv + + elif (b == maxv) : + # couleur entre magenta et cyan + H = 4 + (r - g) / diffv + + # Conversion en degrés + H *= 60 + + # pour éviter une valeur négative + if (H < 0) : + H += 360 + + return [H, L, S] + +def hls_to_rgb (H, L, S): + """ + #--------------------------------------------------------------------------- + # Graphics::hls_to_rgb + # Algorythme de conversion HLS -> RGB + #--------------------------------------------------------------------------- + # paramètres : + # H : (de 0 à 1) valeur de la composante teinte de la couleur HLS + # L : (de 0 à 1) valeur de la composante luminosité de la + # couleur HLS + # S : (de 0 à 1) valeur de la composante saturation + # de la couleur HLS + #--------------------------------------------------------------------------- + """ + (R, G, B) = 0, 0, 0 + (p1, p2) = 0, 0 + + + if (L <= 0.5) : + p2 = L + (L * S) + + else : + p2 = L + S - (L * S) + + p1 = 2.0 * L - p2 + + if (S == 0) : + # couleur a-chromatique (gris) + # R = G = B = L + R = L + G = L + B = L + + else : + # couleurs "Chromatiques" + R = hls_value(p1, p2, H + 120) + G = hls_value(p1, p2, H) + B = hls_value(p1, p2, H - 120) + + return [R, G, B] + +def hls_value(q1, q2, hue): + """ + #--------------------------------------------------------------------------- + # Graphics::hls_value (sous fonction interne hls_to_rgb) + #--------------------------------------------------------------------------- + """ + value = None + + hue = hue % 360 + + if (hue < 60) : + value = q1 + (q2 - q1) * hue / 60 + + elif (hue < 180) : + value = q2 + + elif (hue < 240) : + value = q1 + (q2 - q1) * (240 - hue) / 60 + + else : + value = q1 + + return value + +def hexargbcolor(r, g, b, a = None): + """ + #--------------------------------------------------------------------------- + # Graphics::hexargbcolor + # conversion d'une couleur RGB (255,255,255) au format Zinc '#ffffff' + #--------------------------------------------------------------------------- + """ + hexacolor = "#%02x%02x%02x"% (r, g, b) + if ( a is not None ): + hexacolor = "%s;%d"% (hexacolor, a) + return hexacolor + + + +def hexa2rgb(hexastr): + """ + #--------------------------------------------------------------------------- + # Graphics::hexa2rgb + # conversion d'une couleur au format Zinc '#ffffff' en RGB (255,255,255) + #--------------------------------------------------------------------------- + """ + r, g, b = 0, 0, 0 + regex = re.compile("^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$") + res = regex.match(hexastr) + if res is not None : + r = int(res.group(1), 16) + g = int(res.group(2), 16) + b = int(res.group(3), 16) + return (r/255, g/255, b/255) + else : + raise ValueError("Not a hexa color") + +def get_pattern (filename, **options): + """ + #--------------------------------------------------------------------------- + # RESOURCES GRAPHIQUES PATTERNS, TEXTURES, IMAGES, GRADIENTS, COULEURS... + #--------------------------------------------------------------------------- + #--------------------------------------------------------------------------- + # Graphics::get_pattern + # retourne la ressource bitmap en l'initialisant si première utilisation + #--------------------------------------------------------------------------- + # paramètres : + # filename : nom du fichier bitmap pattern + # options + # -storage : référence de la table de stockage de patterns + #--------------------------------------------------------------------------- + """ + if (options.has_key('storage')): + table = options['storage'] + else : + table = bitmaps + if (not table.has_key(filename)) : + bitmap = "@%s"% (find_inc(filename)) + table[filename] = bitmap + return table[filename] + +def get_texture(widget, filename, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::get_texture + # retourne l'image de texture en l'initialisant si première utilisation + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # filename : nom du fichier texture + # options + # -storage : référence de la table de stockage de textures + #--------------------------------------------------------------------------- + """ + if (options.has_key('storage')): + table = options['storage'] + else : + table = textures + return get_image(widget, filename, storage = table) + +class FileNotFound (Exception): + """ + #--------------------------------------------------------------------------- + # Graphics::FileNotFound + # Classe d'exception levée lorsqu'un fichier n'est pas trouvé + # paramètres : + # filename : nom du fichier + #--------------------------------------------------------------------------- + """ + def __init__(self, filename): + Exception.__init__(self, "File %s not Found"%(filename)) + +def find_inc(name): + """ + #--------------------------------------------------------------------------- + # Graphics::find_inc + # recherche le fichier dans les répertoires de PYTHONPATH + #--------------------------------------------------------------------------- + """ + import sys + import os.path + for path in sys.path: + tfile = os.path.join(path, name) + if (os.path.isfile(tfile)): + return tfile + raise FileNotFound(name) + + +def get_image(zinc, filename, storage = {}): + """ + #--------------------------------------------------------------------------- + # Graphics::get_image + # retourne la ressource image en l'initialisant si première utilisation + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # filename : nom du fichier image + # options + # storage : référence de la table de stockage d'images + #--------------------------------------------------------------------------- + """ + if (not storage.has_key(filename)): + im = PIL.Image.open(find_inc(filename)) + #Cela marche uniquement si Tkinter.Tk a une instance + image = PIL.ImageTk.PhotoImage(im) + storage[filename] = image + return storage[filename] + +def init_pixmaps(widget, *pixfiles, **options): + """ + #--------------------------------------------------------------------------- + # Graphics::init_pixmaps + # initialise une liste de fichier image + #--------------------------------------------------------------------------- + # paramètres : + # widget : identifiant du widget zinc + # filenames : list des noms des fichier image + # options + # storage : référence de la table de stockage d'images + #--------------------------------------------------------------------------- + """ + imgs = [] + for pixfile in pixfiles: + imgs.append(get_image(widget, pixfile, **options)) + return imgs + +def _trunc(f, n): + """ + #--------------------------------------------------------------------------- + # Graphics::_trunc + # fonction interne de troncature des nombres: n = position décimale + #--------------------------------------------------------------------------- + """ + import fpformat + return fpformat.fix(f, n) + +#Local Variables: +#mode : python +#tab-width: 4 +#end: -- cgit v1.1