aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Perl/Zinc/Graphics.pm1317
-rw-r--r--Perl/demos/Tk/demos/zinc_data/paper.gifbin0 -> 1529 bytes
-rw-r--r--Perl/demos/Tk/demos/zinc_lib/testGraphics.pl1707
-rw-r--r--Perl/demos/zinc-demos7
4 files changed, 3029 insertions, 2 deletions
diff --git a/Perl/Zinc/Graphics.pm b/Perl/Zinc/Graphics.pm
new file mode 100644
index 0000000..bd312c8
--- /dev/null
+++ b/Perl/Zinc/Graphics.pm
@@ -0,0 +1,1317 @@
+#!/usr/bin/perl
+#-----------------------------------------------------------------------------------
+#
+# Graphics.pm
+# module de fonctions graphiques
+#
+#-----------------------------------------------------------------------------------
+# Gestion de ressources globales : gradiants zinc (couleurs et dégradés nommés)
+# patterns (fichiers bitmap X11)
+# textures (fichiers texture)
+# images (fichiers GIF, JPEG, PNG)
+#-----------------------------------------------------------------------------------
+# Fonctions Zinc : buildZincItem (réalisation d'un item zinc à partir d'une table de description)
+#
+# Fonctions géométriques : roundedRectangleCoords
+# HippoCoords
+# polygonCoords
+# roundedCurveCoords
+# polylineCoords
+# tabBoxCoords
+# roundedAngle
+# roundedCurve
+# perpendicularPoint
+# lineAngle
+# vertexAngle
+# arc_pts
+# rad_point
+#
+# Fonctions picturales : setGradiants
+# getPattern
+# getTexture
+# getImage
+# init_pixmaps
+#-----------------------------------------------------------------------------------
+# Authors: Jean-Luc Vinot <vinot@cena.fr>
+#
+# $Id:
+#-----------------------------------------------------------------------------------
+package Graphics;
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(&buildZincItem &setGradiants &getTexture &getPattern &getImage &roundedAngle &roundedCurveCoords
+ &polylineCoords &polygonCoords &TabBoxCoords &pathLineCoords &rad_point &arc_pts);
+
+use strict;
+use Carp;
+use Tk;
+use Math::Trig;
+
+# constante facteur point directeur
+my $const_ptd_factor = .5523;
+
+my @Gradiants;
+my %textures;
+my %images;
+my %bitmaps;
+
+my $font_7 = '-cenapii-bleriot mini-book-r-normal--7-70-75-75-p-*-iso8859-15';
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::buildZincItem
+# Création d'un objet Zinc de représentation
+# paramètres :
+# widget : <widget>
+# parentgroup : <group>
+# style : {hash table options}
+# specific_tags : [list of specific tags] to add to params -tags
+# name : <str> nom de l'item
+#-----------------------------------------------------------------------------------
+# type d'item valide :
+# 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
+# -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)
+#-----------------------------------------------------------------------------------
+sub buildZincItem {
+ my ($zinc, $parentgroup, $style, $specific_tags, $name) = @_;
+ $parentgroup = 1 if !$parentgroup;
+
+ my @tags = ($specific_tags) ? @{$specific_tags} : ();
+ my $params_tags;
+
+ if ($style->{'-params'}->{'-tags'}) {
+ $params_tags = delete $style->{'-params'}->{'-tags'};
+ push (@tags, @{$params_tags}) if $params_tags;
+ }
+
+ my $itemtype = $style->{'-itemtype'};
+ my $coords = $style->{'-coords'};
+
+ # création de l'item Zinc
+ my $item;
+
+ # gestion des polygones particuliers et à coin arrondi
+ if ($itemtype eq 'roundedrectangle') {
+ $itemtype = 'curve';
+ $style->{'-params'}->{'-closed'} = 1;
+ $coords = &roundedRectangleCoords($coords, %{$style});
+
+ } elsif ($itemtype eq 'hippodrome') {
+ $itemtype = 'curve';
+ $style->{'-params'}->{'-closed'} = 1;
+ $coords = &HippoCoords($coords, %{$style});
+
+ } elsif ($itemtype eq 'polygone') {
+ $itemtype = 'curve';
+ $style->{'-params'}->{'-closed'} = 1;
+ $coords = &polygonCoords($coords, %{$style});
+
+ } elsif ($itemtype eq 'roundedcurve' or $itemtype eq 'polyline') {
+ $itemtype = 'curve';
+ if ($itemtype eq 'roundedcurve') {
+ $style->{'-params'}->{'-closed'} = 1;
+ $coords = &roundedCurveCoords($coords, %{$style});
+
+ } else {
+ $coords = &polylineCoords($coords, %{$style});
+ }
+
+ # multi-contours
+ if ($style->{'-contours'}) {
+ my @contours = @{$style->{'-contours'}};
+ my $numcontours = scalar(@contours);
+ for (my $i = 0; $i < $numcontours; $i++) {
+ # radius et corners peuvent être défini spécifiquement pour chaque contour
+ my ($type, $way, $coords, $radius, $corners, $corners_radius) = @{$contours[$i]};
+ $radius = $style->{'-radius'} if (!defined $radius);
+ my $newcoords;
+ if ($itemtype eq 'roundedcurve') {
+ $newcoords = &roundedCurveCoords($coords, -radius => $radius, -corners => $corners);
+ } else {
+ $newcoords = &polylineCoords($coords, -radius => $radius, -corners => $corners, -corners_radius => $corners_radius);
+ }
+
+ $style->{'-contours'}->[$i] = [$type, $way, $newcoords];
+ }
+ }
+ } elsif ($itemtype eq 'pathline') {
+ $itemtype = 'triangles';
+ if ($style->{'-metacoords'}) {
+ $coords = &metaCoords(%{$style->{'-metacoords'}});
+
+ }
+
+ if ($style->{'-graduate'}) {
+ my $numcolors = scalar(@{$coords});
+ $style->{'-params'}->{'-colors'} = &pathGraduate($zinc, $numcolors, $style->{'-graduate'});
+ }
+
+ $coords = &pathLineCoords($coords, %{$style});
+
+ }
+
+ if ($itemtype eq 'group') {
+ $item = $zinc->add($itemtype,
+ $parentgroup,
+ %{$style->{'-params'}},
+ -tags => \@tags,
+ );
+
+ $zinc->coords($item, $coords) if $coords;
+
+ } elsif ($itemtype eq 'text' or $itemtype eq 'icon') {
+ my $imagefile;
+ if ($itemtype eq 'icon') {
+ $imagefile = $style->{'-params'}->{'-image'};
+ $style->{'-params'}->{'-image'} = &init_pixmap($zinc, $imagefile) if $imagefile;
+ }
+
+ $item = $zinc->add($itemtype,
+ $parentgroup,
+ -position => $coords,
+ %{$style->{'-params'}},
+ -tags => \@tags,
+ );
+
+ $style->{'-params'}->{'-image'} = $imagefile if $imagefile;
+
+ } else {
+ $item = $zinc->add($itemtype,
+ $parentgroup,
+ $coords,
+ %{$style->{'-params'}},
+ -tags => \@tags,
+ );
+
+ if ($itemtype eq 'curve' and $style->{'-contours'}) {
+ foreach my $contour (@{$style->{'-contours'}}) {
+ $zinc->contour($item, @{$contour});
+ }
+ }
+
+ # gestion du mode norender
+ if ($style->{'-texture'}) {
+ my $texture = &getTexture($zinc, $style->{'-texture'});
+ $zinc->itemconfigure($item, -tile => $texture) if $texture;
+ }
+
+ if ($style->{'-fillpattern'}) {
+ my $bitmap = &getBitmap($style->{'-fillpattern'});
+ $zinc->itemconfigure($item, -fillpattern => $bitmap) if $bitmap;
+ }
+
+
+ }
+
+ # transformation scale de l'item si nécessaire
+ $zinc->scale($item, @{$style->{'-scale'}}) if ($style->{'-scale'});
+
+ # transformation rotate de l'item si nécessaire
+ $zinc->rotate($item, deg2rad($style->{'-rotate'})) if ($style->{'-rotate'});
+
+ # transformation scale de l'item si nécessaire
+ $zinc->translate($item, @{$style->{'-translate'}}) if ($style->{'-translate'});
+
+ # remise étét initial de la table de hash
+ $style->{'-params'}->{'-tags'} = $params_tags if ($params_tags);
+
+ return $item;
+
+}
+
+#-----------------------------------------------------------------------------------
+# FONCTIONS GEOMETRIQUES
+#-----------------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------------
+# Graphics::metaCoords
+# retourne une liste de coordonnées en utilisant la fonction d'un autre type d'item
+# paramètres : (options)
+# -type : type de primitive utilisée
+# -coords : coordonnées nécessitée par la fonction [type]Coords
+# + options spécialisées passés à la fonction [type]Coords
+#-----------------------------------------------------------------------------------
+sub metaCoords {
+ my (%options) = @_;
+ my $pts;
+
+ my @options = keys(%options);
+ my $type = delete $options{'-type'};
+ my $coords = delete $options{'-coords'};
+
+ if ($type eq 'polygone') {
+ $pts = &polygonCoords($coords, %options);
+
+ } elsif ($type eq 'hyppodrome') {
+ $pts = &hippoCoords($coords, %options);
+
+ } elsif ($type eq 'polyline') {
+ $pts = &polylineCoords($coords, %options);
+ }
+
+ return $pts;
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::roundedRectangleCoords
+# calcul des coords du rectangle à coins arrondis
+# paramètres :
+# coords : point centre du polygone
+# options :
+# -radius : rayon de raccord d'angle
+# -corners : liste des raccords de sommets [0 (aucun raccord)|1] par défaut [1,1,1,1]
+#-----------------------------------------------------------------------------------
+sub roundedRectangleCoords {
+ my ($coords, %options) = @_;
+ my ($x0, $y0, $xn, $yn) = ($coords->[0]->[0], $coords->[0]->[1],
+ $coords->[1]->[0], $coords->[1]->[1]);
+
+ my @options = keys(%options);
+ my $radius = $options{'-radius'};
+ my $corners = $options{'-corners'} ? $options{'-corners'} : [1, 1, 1, 1];
+
+ # attention aux formes 'négatives'
+ if ($xn < $x0) {
+ my $xs = $x0;
+ ($x0, $xn) = ($xn, $xs);
+ }
+ if ($yn < $y0) {
+ my $ys = $y0;
+ ($y0, $yn) = ($yn, $ys);
+ }
+
+ my $height = &_min($xn -$x0, $yn - $y0);
+
+ if (!defined $radius) {
+ $radius = int($height/10);
+ $radius = 3 if $radius < 3;
+ }
+
+ if (!$radius or $radius < 2) {
+ return [[$x0, $y0],[$x0, $yn],[$xn, $yn],[$xn, $y0]];
+
+ }
+
+
+ # correction de radius si necessaire
+ my $max_rad = $height;
+ $max_rad /= 2 if (!defined $corners);
+ $radius = $max_rad if $radius > $max_rad;
+
+ # points remarquables
+ my $ptd_delta = $radius * $const_ptd_factor;
+ my ($x2, $x3) = ($x0 + $radius, $xn - $radius);
+ my ($x1, $x4) = ($x2 - $ptd_delta, $x3 + $ptd_delta);
+ my ($y2, $y3) = ($y0 + $radius, $yn - $radius);
+ my ($y1, $y4) = ($y2 - $ptd_delta, $y3 + $ptd_delta);
+
+ # liste des 4 points sommet du rectangle : angles sans raccord circulaire
+ my @angle_pts = ([$x0, $y0],[$x0, $yn],[$xn, $yn],[$xn, $y0]);
+
+ # liste des 4 segments quadratique : raccord d'angle = radius
+ my @roundeds = ([[$x2, $y0],[$x1, $y0, 'c'],[$x0, $y1, 'c'],[$x0, $y2],],
+ [[$x0, $y3],[$x0, $y4, 'c'],[$x1, $yn, 'c'],[$x2, $yn],],
+ [[$x3, $yn],[$x4, $yn, 'c'],[$xn, $y4, 'c'],[$xn, $y3],],
+ [[$xn, $y2],[$xn, $y1, 'c'],[$x4, $y0, 'c'],[$x3, $y0],]);
+
+ my @pts = ();
+ for (my $i = 0; $i < 4; $i++) {
+ if ($corners->[$i]) {
+ push(@pts, @{$roundeds[$i]});
+
+ } else {
+ push(@pts, $angle_pts[$i]);
+ }
+ }
+
+ return \@pts;
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::HippoCoords
+# calcul des coords d'un hippodrome
+# paramètres :
+# coords : coordonnées du rectangle exinscrit
+# options :
+# -orientation : orientation forcée de l'ippodrome [horizontal|vertical]
+# -corners : liste des raccords de sommets [0|1] par défaut [1,1,1,1]
+# -trunc : troncatures [left|right|top|bottom|both]
+#-----------------------------------------------------------------------------------
+sub HippoCoords {
+ my ($coords, %options) = @_;
+ my ($x0, $y0, $xn, $yn) = ($coords->[0]->[0], $coords->[0]->[1],
+ $coords->[1]->[0], $coords->[1]->[1]);
+
+ my @options = keys(%options);
+ my $orientation = $options{'-orientation'};
+
+ # orientation forcée de l'hippodrome (sinon hippodrome sur le plus petit coté)
+ my $height = ($orientation eq 'horizontal') ? abs($yn - $y0)
+ : ($orientation eq 'vertical') ? abs($xn - $x0) : &_min(abs($xn - $x0), abs($yn - $y0));
+ my $radius = $height/2;
+ my $corners = [1, 1, 1, 1];
+
+ if ($options{'-corners'}) {
+ $corners = $options{'-corners'};
+
+ } elsif ($options{'-trunc'}) {
+ my $trunc = $options{'-trunc'};
+ if ($trunc eq 'both') {
+ return [[$x0, $y0],[$x0, $yn],[$xn, $yn],[$xn, $y0]];
+
+ } else {
+ $corners = ($trunc eq 'left') ? [0, 0, 1, 1] :
+ ($trunc eq 'right') ? [1, 1, 0, 0] :
+ ($trunc eq 'top') ? [0, 1, 1, 0] :
+ ($trunc eq 'bottom') ? [1, 0, 0, 1] : [1, 1, 1, 1];
+
+ }
+ }
+
+ # l'hippodrome est un cas particulier de roundedRectangle
+ # on retourne en passant la 'configuration' à la fonction générique roundedRectangleCoords
+ return &roundedRectangleCoords($coords, -radius => $radius, -corners => $corners);
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::polygonCoords
+# 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 du polygone
+#-----------------------------------------------------------------------------------
+sub polygonCoords {
+ my ($coords, %options) = @_;
+
+ my @options = keys(%options);
+ my $numsides = $options{'-numsides'};
+ my $radius = $options{'-radius'};
+ if ($numsides < 3 or !$radius) {
+ print "Vous devez au moins spécifier un nombre de cotés >= 3 et un rayon...\n";
+ return undef;
+ }
+
+ my ($cx, $cy) = ($coords) ? @{$coords} : (0, 0);
+ my $startangle = ($options{'-startangle'}) ? $options{'-startangle'} : 0;
+ my $anglestep = 360/$numsides;
+ my $inner_radius = $options{'-inner_radius'};
+ my @pts;
+
+ # points du polygone
+ for (my $i = 0; $i < $numsides; $i++) {
+ my ($xp, $yp) = &rad_point($cx, $cy, $radius, $startangle + ($anglestep*$i));
+ push(@pts, ([$xp, $yp]));
+
+ # polygones 'étoiles'
+ if ($inner_radius) {
+ ($xp, $yp) = &rad_point($cx, $cy, $inner_radius, $startangle + ($anglestep*($i+ 0.5)));
+ push(@pts, ([$xp, $yp]));
+ }
+ }
+
+
+ if ($options{'-corner_radius'}) {
+ return &roundedCurveCoords(\@pts, -radius => $options{'-corner_radius'}, -corners => $options{'-corners'});
+ } else {
+ return \@pts;
+ }
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::roundedAngle
+# curve d'angle avec raccord circulaire
+# paramètres :
+# zinc : widget
+# parentgroup : group zinc parent
+# coords : les 3 points de l'angle
+# radius : rayon de raccord
+#-----------------------------------------------------------------------------------
+sub roundedAngle {
+ my ($zinc, $parentgroup, $coords, $radius) = @_;
+ my ($pt0, $pt1, $pt2) = @{$coords};
+
+ my ($corner_pts, $center_pts) = &roundedAngleCoords($coords, $radius);
+ my ($cx0, $cy0) = @{$center_pts};
+
+ # valeur d'angle et angle formé par la bisectrice
+ my ($angle) = &vertexAngle($pt0, $pt1, $pt2);
+
+ $parentgroup = 1 if (!defined $parentgroup);
+
+ # temporaire -> visibilité de l'arc 'conique' correspondant
+ $zinc->add('arc', $parentgroup,
+ [[$cx0 - $radius, $cy0 - $radius],[$cx0 + $radius, $cy0 + $radius]],
+ -priority => 10,
+ -linecolor => '#9999ff',
+ -filled => 0,
+ -visible => 1,
+ -linewidth => 2,
+ );
+
+ # temporaire
+ $zinc->add('text', $parentgroup,
+ -position => [$cx0 - $radius + 2, $cy0],
+ -priority => 20,
+ -color => '#0000ff',
+ -text => $angle."°",
+ -font => $font_7,
+ -alignment => 'left',
+ -anchor => 'w',
+ );
+
+ $zinc->add('curve', $parentgroup,
+ [$pt0,@{$corner_pts},$pt2],
+ -closed => 0,
+ -linewidth => 1,
+ -priority => 20,
+ );
+
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::roundedAngleCoords
+# 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
+#-----------------------------------------------------------------------------------
+sub roundedAngleCoords {
+ my ($coords, $radius) = @_;
+ my ($pt0, $pt1, $pt2) = @{$coords};
+
+ # valeur d'angle et angle formé par la bisectrice
+ my ($angle, $bisecangle) = &vertexAngle($pt0, $pt1, $pt2);
+
+ # distance au centre du cercle inscrit : rayon/sinus demi-angle
+ my $sin = sin(deg2rad($angle/2));
+ my $delta = ($sin) ? abs($radius / $sin) : $radius;
+
+ # point centre du cercle inscrit de rayon $radius
+ my $refangle = ($angle < 180) ? $bisecangle+90 : $bisecangle-90;
+ my ($cx0, $cy0) = rad_point($pt1->[0], $pt1->[1], $delta, $refangle);
+
+ # points de tangeance : pts perpendiculaires du centre aux 2 droites
+ my ($px1, $py1) = &perpendicularPoint([$cx0, $cy0], [$pt0, $pt1]);
+ my ($px2, $py2) = &perpendicularPoint([$cx0, $cy0], [$pt1, $pt2]);
+
+ # point de controle de la quadratique
+ # facteur de positionnement sur le vecteur pt.tangence, sommet
+ my $ptd_factor = $const_ptd_factor;
+ if ($angle < 90 or $angle > 270) {
+ my $diffangle = ($angle < 90) ? $angle : 360 - $angle;
+ $ptd_factor -= (((90 - $diffangle)/90) * ($ptd_factor/4)) if $diffangle > 15 ;
+ $ptd_factor = ($diffangle/90) * ($ptd_factor + ((1 - $ptd_factor) * (90 - $diffangle)/90));
+ } else {
+ my $diffangle = abs(180 - $angle);
+ $ptd_factor += (((90 - $diffangle)/90) * ($ptd_factor/3)) if $diffangle > 15;
+ }
+
+ # delta xy aux pts de tangence
+ my ($d1x, $d1y) = (($pt1->[0] - $px1) * $ptd_factor, ($pt1->[1] - $py1) * $ptd_factor);
+ my ($d2x, $d2y) = (($pt1->[0] - $px2) * $ptd_factor, ($pt1->[1] - $py2) * $ptd_factor);
+
+ # les 4 points de l'arc 'quadratique'
+ my $corner_pts = [[$px1, $py1],[$px1+$d1x, $py1+$d1y, 'c'],
+ [$px2+$d2x, $py2+$d2y, 'c'],[$px2, $py2]];
+
+
+ # retourne le segment de quadratique et le centre du cercle inscrit
+ return ($corner_pts, [$cx0, $cy0]);
+
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::roundedCurveCoords
+# retourne les coordonnées d'une curve à coins arrondis
+# paramètres :
+# coords : 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]
+#-----------------------------------------------------------------------------------
+sub roundedCurveCoords {
+ my ($coords, %options) = @_;
+ my $numfaces = scalar(@{$coords});
+ my @curve_pts;
+
+ my @options = keys(%options);
+ my $radius = ($options{'-radius'}) ? $options{'-radius'} : 0;
+ my $corners = $options{'-corners'};
+
+ for (my $index = 0; $index < $numfaces; $index++) {
+ if ($corners and !$corners->[$index]) {
+ push(@curve_pts, $coords->[$index]);
+
+ } else {
+ my $prev = ($index) ? $index - 1 : $numfaces - 1;
+ my $next = ($index > $numfaces - 2) ? 0 : $index + 1;
+ my $anglecoords = [$coords->[$prev], $coords->[$index], $coords->[$next]];
+
+ my ($quad_pts) = &roundedAngleCoords($anglecoords, $radius);
+ push(@curve_pts, @{$quad_pts});
+ }
+ }
+
+ return \@curve_pts;
+
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::polylineCoords
+# retourne les coordonnées d'une polyline
+# paramètres :
+# coords : 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
+#-----------------------------------------------------------------------------------
+sub polylineCoords {
+ my ($coords, %options) = @_;
+ my $numfaces = scalar(@{$coords});
+ my @curve_pts;
+
+ my @options = keys(%options);
+ my $radius = ($options{'-radius'}) ? $options{'-radius'} : 0;
+ my $corners_radius = $options{'-corners_radius'};
+ my $corners = ($corners_radius) ? $corners_radius : $options{'-corners'};
+
+ for (my $index = 0; $index < $numfaces; $index++) {
+ if ($corners and !$corners->[$index]) {
+ push(@curve_pts, $coords->[$index]);
+
+ } else {
+ my $prev = ($index) ? $index - 1 : $numfaces - 1;
+ my $next = ($index > $numfaces - 2) ? 0 : $index + 1;
+ my $anglecoords = [$coords->[$prev], $coords->[$index], $coords->[$next]];
+
+ my $rad = ($corners_radius) ? $corners_radius->[$index] : $radius;
+ my ($quad_pts) = &roundedAngleCoords($anglecoords, $rad);
+ push(@curve_pts, @{$quad_pts});
+ }
+ }
+
+ return \@curve_pts;
+
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::pathLineCoords
+# retourne les coordonnées d'une pathLine
+# paramètres :
+# coords : points de path
+# options :
+# -closed : ligne fermée
+# -shifting : sens de décalage [both|left|right] par défaut both
+# -linewidth : epaisseur de la ligne
+#-----------------------------------------------------------------------------------
+sub pathLineCoords {
+ my ($coords, %options) = @_;
+ my $numfaces = scalar(@{$coords});
+ my @pts;
+
+ my @options = keys(%options);
+ my $closed = $options{'-closed'};
+ my $linewidth = ($options{'-linewidth'}) ? $options{'-linewidth'} : 0;
+ my $shifting = ($options{'-shifting'}) ? $options{'-shifting'} : 'both';
+
+ return undef if (!$numfaces or $linewidth < 2);
+
+ my $previous = ($closed) ? $coords->[$numfaces - 1] : undef;
+ my $next = $coords->[1];
+ $linewidth /= 2 if ($shifting eq 'both');
+
+ for (my $i = 0; $i < $numfaces; $i++) {
+ my $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])];
+ }
+
+ my ($angle, $bisecangle) = &vertexAngle($previous, $pt, $next);
+
+ # distance au centre du cercle inscrit : rayon/sinus demi-angle
+ my $sin = sin(deg2rad($angle/2));
+ my $delta = ($sin) ? abs($linewidth / $sin) : $linewidth;
+
+ if ($shifting eq 'left' or $shifting eq 'right') {
+ my $adding = ($shifting eq 'left') ? 90 : -90;
+ push (@pts, &rad_point($pt->[0], $pt->[1], $delta, $bisecangle + $adding));
+ push (@pts, @{$pt});
+
+ } else {
+ push (@pts, &rad_point($pt->[0], $pt->[1], $delta, $bisecangle+90));
+ push (@pts, &rad_point($pt->[0], $pt->[1], $delta, $bisecangle-90));
+
+ }
+
+ if ($i == $numfaces - 2) {
+ $next = ($closed) ? $coords->[0] :
+ [$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) {
+ push (@pts, ($pts[0], $pts[1], $pts[2], $pts[3]));
+ }
+
+ return \@pts;
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::perpendicularPoint
+# retourne les coordonnées du point perpendiculaire abaissé d'un point sur une ligne
+#-----------------------------------------------------------------------------------
+sub perpendicularPoint {
+ my ($point, $line) = @_;
+ my ($p1, $p2) = @{$line};
+
+ # cas partiuculier de lignes ortho.
+ my $min_dist = .01;
+ if (abs($p2->[1] - $p1->[1]) < $min_dist) {
+ # la ligne de référence est horizontale
+ return ($point->[0], $p1->[1]);
+
+ } elsif (abs($p2->[0] - $p1->[0]) < $min_dist) {
+ # la ligne de référence est verticale
+ return ($p1->[0], $point->[1]);
+ }
+
+ my $a1 = ($p2->[1] - $p1->[1]) / ($p2->[0] - $p1->[0]);
+ my $b1 = $p1->[1] - ($a1 * $p1->[0]);
+
+ my $a2 = -1.0 / $a1;
+ my $b2 = $point->[1] - ($a2 * $point->[0]);
+
+ my $x = ($b2 - $b1) / ($a1 - $a2);
+ my $y = ($a1 * $x) + $b1;
+
+ return ($x, $y);
+
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::lineAngle
+# retourne l'angle d'un point par rapport à un centre de référence
+#-----------------------------------------------------------------------------------
+sub lineAngle {
+ my ($x, $y, $xref, $yref) = @_;
+ my $angle = atan2($y - $yref, $x - $xref);
+
+ $angle += pi/2;
+ $angle *= 180/pi;
+ $angle += 360 if ($angle < 0);
+
+ return $angle;
+
+}
+
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::vertexAngle
+# retourne la valeur de l'angle formée par 3 points
+# ainsi que l'angle de la bisectrice
+#-----------------------------------------------------------------------------------
+sub vertexAngle {
+ my ($pt0, $pt1, $pt2) = @_;
+ my $angle1 = &lineAngle(@{$pt1}, @{$pt0});
+ my $angle2 = &lineAngle(@{$pt1}, @{$pt2});
+
+ $angle2 += 360 if $angle2 < $angle1;
+ my $alpha = $angle2 - $angle1;
+ my $bisectrice = $angle1 + ($alpha/2);
+
+ return ($alpha, $bisectrice);
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::arc_pts
+# calcul des points constitutif d'un arc
+# params : x,y centre, rayon, angle départ, delta angulaire, pas en degré
+#-----------------------------------------------------------------------------------
+sub arc_pts {
+ my ($x, $y, $rad, $angle, $extent, $step, $debug) = @_;
+ my @pts = ();
+
+
+ if ($extent > 0) {
+ for (my $alpha = $angle; $alpha <= ($angle + $extent); $alpha += $step) {
+ my ($xn, $yn) = &rad_point($x, $y, $rad,$alpha);
+ push (@pts, ([$xn, $yn]));
+ }
+ } else {
+ for (my $alpha = $angle; $alpha >= ($angle + $extent); $alpha += $step) {
+ push (@pts, &rad_point($x, $y, $rad,$alpha));
+ }
+ }
+
+ return @pts;
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::rad_point
+# retourne le point circulaire défini par centre-rayon-angle
+#-----------------------------------------------------------------------------------
+sub rad_point {
+ my ($x, $y, $rad, $angle) = @_;
+ my $alpha = deg2rad($angle);
+
+ my $xpt = $x + ($rad * cos($alpha));
+ my $ypt = $y + ($rad * sin($alpha));
+
+ return ($xpt, $ypt);
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::buildTabBox
+# création des items Zinc d'un ensemble de page à onglet
+#-----------------------------------------------------------------------------------
+sub buidTabBox {
+ my ($zinc, $group, $style, $specific_tags,) = @_;
+
+ # création d'un groupe principal si besoin
+ my $groupstyle = delete $style->{'-group'};
+ if ($groupstyle) {
+ $group = &buildZincItem($zinc, $group, $groupstyle);
+
+ } else {
+ $group = 1 if (!defined $group);
+ }
+
+ # calcul des shapes
+ my $coords = $style->{'-coords'};
+ my $params = $style->{'-params'};
+ my $multi = $style->{'-multi'};
+ my $titles = $style->{'-titles'};
+
+ my ($shapes, $title_coords) = &computeDividers($coords,%{$style});
+
+ # création des intercalaires
+ my $i = scalar(@{$shapes}) - 1;
+ foreach my $shape (reverse @{$shapes}) {
+ if ($multi) {
+ while (my ($key, $values) = each(%{$multi})) {
+ $params->{$key} = $values->[$i];
+ }
+ }
+
+ # item zinc enveloppe intercalaire
+ my $intergroup = $zinc->add('group', $group);
+ my %interstyle = (-itemtype => 'curve',
+ -closed => 1,
+ -coords => $shape,
+ -params => $params,
+ );
+ $interstyle{-texture} = $style->{'-texture'} if ($style->{'-texture'});
+ my $inter = &buildZincItem($zinc, $intergroup, \%interstyle, $specific_tags);
+
+ # titre de l'onglet
+ if ($titles) {
+ my $params = $titles->{'-params'};
+ $coords = ($titles->{'-coords'}) ? $titles->{'-coords'}->[$i] : $title_coords->[$i];
+ $params->{'-text'} = $titles->{'-text'}->[$i];
+ $zinc->add('text', $intergroup,
+ -position => $coords,
+ %{$params},
+ );
+ }
+
+ # zone page interne à l'intercalaire
+ if ($style->{'-page'}) {
+ &buildZincItem($zinc, $intergroup, $style->{'-page'});
+ }
+
+ # items complémentaires
+ if ($style->{'-decos'}) {
+ while (my ($itemname, $itemstyle) = each(%{$style->{'-decos'}})) {
+ &buildZincItem($zinc, $intergroup, $itemstyle);
+ }
+ }
+
+ $i--;
+ }
+}
+
+#-----------------------------------------------------------------------------------
+# TabBoxCoords
+# Calcul des shapes de boites à onglets
+#
+# coords : coordonnées rectangle de la bounding box
+#
+# options
+# -numpages <n> : nombre de pages (onglets) de la boite
+# -anchor [n|e|s|w] : ancrage des onglets
+# -alignment [left|center|right] : alignement des onglets sur le coté d'ancrage
+# -tabwidth [<n>|[<n1>,<n2>,<n3>...]|auto] : largeur des onglets
+# -tabheight [<n>|auto] : hauteur des onglets
+# -tabshift <n> : décalage onglet
+# -radius <n> : rayon des arrondis d'angle
+# -overlap <n> : distance de recouvrement des onglets
+#-----------------------------------------------------------------------------------
+sub TabBoxCoords {
+ my ($coords, %options) = @_;
+ my ($x0, $y0, $xn, $yn) = (@{$coords->[0]}, @{$coords->[1]});
+ my (@shapes, @titles_coords);
+ my $inverse;
+
+ my @options = keys(%options);
+ my $numpages = $options{'-numpages'};
+
+ if (!defined $x0 or !defined $y0 or !defined $xn or !defined $yn or !$numpages) {
+ print "Vous devez au minimum spécifier le rectangle englobant et le nombre de pages\n";
+ return undef;
+
+ }
+
+ my $anchor = ($options{'-anchor'}) ? $options{'-anchor'} : 'n';
+ my $alignment = ($options{'-alignment'}) ? $options{'-alignment'} : 'left';
+ my $len = ($options{'-tabwidth'}) ? $options{'-tabwidth'} : 'auto';
+ my $thick = ($options{'-tabheight'}) ? $options{'-tabheight'} : 'auto';
+ my $biso = ($options{'-tabshift'}) ? $options{'-tabshift'} : 'auto';
+ my $radius = ($options{'-radius'}) ? $options{'-radius'} : 0;
+ my $overlap = ($options{'-overlap'}) ? $options{'-overlap'} : 0;
+ my $orientation = ($anchor eq 'n' or $anchor eq 's') ? 'horizontal' : 'vertical';
+ my $maxwidth = ($orientation eq 'horizontal') ? ($xn - $x0) : ($yn - $y0);
+ my $tabswidth = 0;
+ my $align = 1;
+
+ if ($len eq 'auto') {
+ $tabswidth = $maxwidth;
+ $len = ($tabswidth + ($overlap * ($numpages - 1)))/$numpages;
+
+ } else {
+ if (ref($len) eq 'ARRAY') {
+ foreach my $w (@{$len}) {
+ $tabswidth += ($w - $overlap);
+ }
+ $tabswidth += $overlap;
+ } else {
+ $tabswidth = ($len * $numpages) - ($overlap * ($numpages - 1));
+ }
+
+ if ($tabswidth > $maxwidth) {
+ $tabswidth = $maxwidth;
+ $len = ($tabswidth + ($overlap * ($numpages - 1)))/$numpages;
+ }
+
+ $align = 0 if ($alignment eq 'center' and (($maxwidth - $tabswidth) > $radius));
+ }
+
+
+ if ($thick eq 'auto') {
+ $thick = ($orientation eq 'horizontal') ? int(($yn - $y0)/10) : int(($xn - $y0)/10);
+ $thick = 10 if ($thick < 10);
+ $thick = 40 if ($thick > 40);
+ }
+
+ if ($biso eq 'auto') {
+ $biso = int($thick/2);
+ }
+
+ if (($alignment eq 'right' and $anchor ne 'w') or
+ ($anchor eq 'w' and $alignment ne 'right')) {
+
+ if (ref($len) eq 'ARRAY') {
+ for (my $i = 0; $i < $numpages; $i++) {
+ $len->[$i] *= -1;
+ }
+ } else {
+ $len *= -1;
+ }
+ $biso *= -1;
+ $overlap *= -1;
+ }
+
+ my ($biso1, $biso2) = ($alignment eq 'center') ? ($biso/2, $biso/2) : (0, $biso);
+
+ my (@cadre, @tabdxy);
+ my ($xref, $yref);
+ if ($orientation eq 'vertical') {
+ $thick *= -1 if ($anchor eq 'w');
+ my ($startx, $endx) = ($anchor eq 'w') ? ($x0, $xn) : ($xn, $x0);
+ my ($starty, $endy) = (($anchor eq 'w' and $alignment ne 'right') or
+ ($anchor eq 'e' and $alignment eq 'right')) ?
+ ($yn, $y0) : ($y0, $yn);
+
+ $xref = $startx - $thick;
+ $yref = $starty;
+ if ($alignment eq 'center') {
+ my $ratio = ($anchor eq 'w') ? -2 : 2;
+ $yref += (($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 ne 'right');
+
+ } else {
+ $thick *= -1 if ($anchor eq 's');
+ my ($startx, $endx) = ($alignment eq 'right') ? ($xn, $x0) : ($x0, $xn);
+ my ($starty, $endy) = ($anchor eq 's') ? ($yn, $y0) : ($y0, $yn);
+
+
+ $yref = $starty + $thick;
+ $xref = ($alignment eq 'center') ? $x0 + (($maxwidth - $tabswidth)/2) : $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 eq 'n' and $alignment ne 'right') or ($anchor eq 's' and $alignment eq 'right'));
+ }
+
+
+ for (my $i = 0; $i < $numpages; $i++) {
+ my @pts = ();
+
+ # décrochage onglet
+ #push (@pts, ([$xref, $yref])) if $i > 0;
+
+ # cadre
+ push (@pts, @cadre);
+
+ # points onglets
+ push (@pts, ([$xref, $yref])) if ($i > 0 or !$align);
+
+ my $tw = (ref($len) eq 'ARRAY') ? $len->[$i] : $len;
+ @tabdxy = ($orientation eq 'vertical') ?
+ ([$thick, $biso1],[$thick, $tw - $biso2],[0, $tw]) : ([$biso1, -$thick],[$tw - $biso2, -$thick],[$tw, 0]);
+ foreach my $dxy (@tabdxy) {
+ push (@pts, ([$xref + $dxy->[0], $yref + $dxy->[1]]));
+ }
+
+
+ if ($radius) {
+ my $corners = ($i > 0 or !$align) ? [0, 1, 1, 1, 0, 1, 1, 0] : [0, 1, 1, 0, 1, 1, 0, 0, 0];
+ my $curvepts = &roundedCurveCoords(\@pts, -radius => $radius, -corners => $corners);
+ @{$curvepts} = reverse @{$curvepts} if ($inverse);
+ push (@shapes, $curvepts);
+ } else {
+ reverse @pts if ($inverse);
+ push (@shapes, \@pts);
+ }
+
+ if ($orientation eq 'horizontal') {
+ push (@titles_coords, [$xref + ($tw - ($biso2 - $biso1))/2, $yref - ($thick/2)]);
+ $xref += ($tw - $overlap);
+
+ } else {
+ push (@titles_coords, [$xref + ($thick/2), $yref + ($len - (($biso2 - $biso1)/2))/2]);
+ $yref += ($len - $overlap);
+ }
+
+ }
+
+ return (\@shapes, \@titles_coords, $inverse);
+
+}
+
+
+
+#-----------------------------------------------------------------------------------
+# RESOURCES GRAPHIQUES GRADIANTS, PATTERNS, TEXTURES, IMAGES...
+#-----------------------------------------------------------------------------------
+#-----------------------------------------------------------------------------------
+# Graphics::setGradiants
+# création de gradiant nommés Zinc
+#-----------------------------------------------------------------------------------
+sub setGradiants {
+ my ($zinc, $grads) = @_;
+
+ # initialise les gradiants de taches
+ unless (@Gradiants) {
+ while (my ($name, $gradiant) = each( %{$grads})) {
+ # création des gradiants nommés
+ $zinc->gname($gradiant, $name);
+ push(@Gradiants, $name);
+ }
+ }
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::getPattern
+# retourne la ressource bitmap en l'initialisant si première utilisation
+#-----------------------------------------------------------------------------------
+sub getPattern {
+ my ($name) = @_;
+ my $bitmap;
+
+ if (!exists($bitmaps{$name})) {
+ $bitmap = '@'.Tk::findINC($name);
+ $bitmaps{$name} = $bitmap;
+
+ } else {
+ $bitmap = $bitmaps{$name};
+ }
+
+ return $bitmap;
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::getTexture
+# retourne l'image de texture en l'initialisant si première utilisation
+#-----------------------------------------------------------------------------------
+sub getTexture {
+ my ($zinc, $name) = @_;
+ my $texture;
+
+ if (!exists($textures{$name})) {
+ $texture = $zinc->Photo(-file => Tk::findINC($name));
+ $textures{$name} = $texture;
+
+ } else {
+ $texture = $textures{$name};
+ }
+
+ return $texture;
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::getImage
+# retourne la ressource image en l'initialisant si première utilisation
+#-----------------------------------------------------------------------------------
+sub getImage {
+ my ($widget, $imagefile) = @_;
+
+ if (!exists($images{$imagefile})) {
+ my $image = $widget->Photo(-file => Tk::findINC($imagefile));
+ $images{$imagefile} = $image if $image;
+ return $image;
+ }
+
+ return $images{$imagefile};
+
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::init_pixmaps
+# initialise une liste de fichier image
+#-----------------------------------------------------------------------------------
+sub init_pixmaps {
+ my ($widget, @pixfiles) = @_;
+ my @imgs = ();
+
+
+ foreach (@pixfiles) {
+ push(@imgs, &getImage($widget, $_));
+ }
+
+ return @imgs;
+}
+
+
+sub _min {
+ my ($n1, $n2) = @_;
+ my $mini = ($n1 > $n2) ? $n2 : $n1;
+ return $mini;
+
+}
+
+sub _max {
+ my ($n1, $n2) = @_;
+ my $maxi = ($n1 > $n2) ? $n1 : $n2;
+ return $maxi;
+
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::_trunc
+# fonction interne de troncature des nombres: n = position décimale
+#-----------------------------------------------------------------------------------
+sub _trunc {
+ my ($val, $n) = @_;
+ my $str;
+ my $dec;
+
+ ($val) =~ /([0-9]+)\.?([0-9]*)/;
+ $str = ($val < 0) ? "-$1" : $1;
+
+ if (($2 ne "") && ($n != 0)) {
+ $dec = substr($2, 0, $n);
+ if ($dec != 0) {
+ $str = $str . "." . $dec;
+ }
+ }
+ return $str;
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::RGB_dec2hex
+# conversion d'une couleur RGB (255,255,255) au format Zinc '#ffffff'
+#-----------------------------------------------------------------------------------
+sub RGB_dec2hex {
+ my (@rgb) = @_;
+ return (sprintf("#%04x%04x%04x", @rgb));
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::pathGraduate
+# création d'un jeu de couleurs dégradées pour item pathLine
+#-----------------------------------------------------------------------------------
+sub pathGraduate {
+ my ($zinc, $numcolors, $style) = @_;
+
+ my $type = $style->{'-type'};
+ my $triangles_colors;
+ if ($type eq 'linear') {
+ return &createGraduate($zinc, $numcolors, $style->{'-colors'}, 2);
+
+ } elsif ($type eq 'double') {
+ my $colors1 = &createGraduate($zinc, $numcolors/2+1, $style->{'-colors'}->[0]);
+ my $colors2 = &createGraduate($zinc, $numcolors/2+1, $style->{'-colors'}->[1]);
+ my @colors;
+ for (my $i = 0; $i <= $numcolors; $i++) {
+ push(@colors, ($colors1->[$i], $colors2->[$i]));
+ }
+
+ return \@colors;
+
+ } elsif ($type eq 'transversal') {
+ my ($c1, $c2) = @{$style->{'-colors'}};
+ my @colors = ($c1, $c2);
+ for (my $i = 0; $i < $numcolors; $i++) {
+ push(@colors, ($c1, $c2));
+ }
+
+ return \@colors;
+ }
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::createGraduate
+# création d'un jeu de couleurs intermédiaires (dégradé) entre n couleurs
+#-----------------------------------------------------------------------------------
+sub createGraduate {
+ my ($widget, $totalsteps, $refcolors, $repeat) = @_;
+ my @colors;
+ my $i = 0;
+
+ $repeat = 1 if (!$repeat);
+ my $numgraduates = scalar @{$refcolors} - 1;
+
+ if ($numgraduates < 1) {
+ print "Le dégradé necessite au minimum 2 couleurs de référence...\n";
+ return undef;
+ }
+
+ my $steps = ($numgraduates > 1) ? $totalsteps/($numgraduates -1) : $totalsteps;
+
+ for (my $c = 0; $c < $numgraduates; $c++) {
+ my ($c1, $c2) = ($refcolors->[$c], $refcolors->[$c+1]);
+
+ for (my $i = 0 ; $i < $steps ; $i++) {
+ my $color = computeColor($widget, $c1, $c2, $i/($steps-1));
+ for (my $k = 0; $k < $repeat; $k++) {
+ push (@colors, $color);
+ }
+ }
+
+ if ($c < $numgraduates - 1) {
+ for (my $k = 0; $k < $repeat; $k++) {
+ pop @colors;
+ }
+ }
+ }
+ return \@colors;
+}
+
+
+#-----------------------------------------------------------------------------------
+# Graphics::computeColor
+# calcul d'une couleur intermédiaire défini par un ratio ($rate) entre 2 couleur
+#-----------------------------------------------------------------------------------
+sub computeColor {
+ my ($widget, $color0, $color1, $rate) = @_;
+ $rate = 1 if ($rate > 1);
+ $rate = 0 if ($rate < 0);
+
+ my ($r0, $g0, $b0, $a0) = &ZnColorToRGB($color0);
+ my ($r1, $g1, $b1, $a1) = &ZnColorToRGB($color1);
+
+ my $r = $r0 + int(($r1 - $r0) * $rate);
+ my $g = $g0 + int(($g1 - $g0) * $rate);
+ my $b = $b0 + int(($b1 - $b0) * $rate);
+ my $a = $a0 + int(($a1 - $a0) * $rate);
+
+ return &hexaRGBcolor($r, $g, $b, $a);
+}
+
+sub ZnColorToRGB {
+ my ($zncolor) = @_;
+
+ my ($color, $alpha) = split /;/, $zncolor;
+ my $ndigits = (length($color) > 8) ? 4 : 2;
+ my $R = hex(substr($color, 1, $ndigits));
+ my $G = hex(substr($color, 1+$ndigits, $ndigits));
+ my $B = hex(substr($color, 1+($ndigits*2), $ndigits));
+
+ $alpha = 100 if ($alpha eq "");
+
+ return ($R, $G, $B, $alpha);
+
+}
+
+#-----------------------------------------------------------------------------------
+# Graphics::hexaRGBcolor
+# conversion d'une couleur RGB (255,255,255) au format Zinc '#ffffff'
+#-----------------------------------------------------------------------------------
+sub hexaRGBcolor {
+ my ($r, $g, $b, $a) = @_;
+
+ if (defined $a) {
+ my $hexacolor = sprintf("#%02x%02x%02x", ($r, $g, $b));
+ return ($hexacolor.";".$a);
+ }
+
+ return (sprintf("#%02x%02x%02x ", ($r, $g, $b)));
+}
+
+1;
+
diff --git a/Perl/demos/Tk/demos/zinc_data/paper.gif b/Perl/demos/Tk/demos/zinc_data/paper.gif
new file mode 100644
index 0000000..3247d35
--- /dev/null
+++ b/Perl/demos/Tk/demos/zinc_data/paper.gif
Binary files differ
diff --git a/Perl/demos/Tk/demos/zinc_lib/testGraphics.pl b/Perl/demos/Tk/demos/zinc_lib/testGraphics.pl
new file mode 100644
index 0000000..c79ace0
--- /dev/null
+++ b/Perl/demos/Tk/demos/zinc_lib/testGraphics.pl
@@ -0,0 +1,1707 @@
+#!/usr/bin/perl
+#-----------------------------------------------------------------------------------
+#
+# testGraphics.pl
+# Fichier test du module Graphics
+#
+# Authors: Jean-Luc Vinot <vinot@cena.fr>
+#
+# $Id:
+#-----------------------------------------------------------------------------------
+
+use Tk;
+use Tk::Zinc;
+use Graphics;
+use Getopt::Long;
+use strict 'vars';
+
+my $rotate_angle = .1;
+my $zoomfactor = .1;
+my $curview;
+my ($dx, $dy);
+
+my $tabanchor = 'n';
+my $tabalign = 'left';
+
+my $font_9b = '-cenapii-bleriot mini-bold-r-normal--9-90-75-75-p-75-iso8859-15';
+
+my %gradset = (# gradiants zinc
+ 'boitonglet' => '=axial 0|#ff7777|#ffff99',
+ 'roundrect1' => '=axial 270|#a7ffa7;70 0|#ffffff;90 5|#00bd00;80 8|#b7ffb7;50 80|#ffffff;70 91|#00ac00;70 95|#006700;60 100',
+ 'roundrect2' => '=axial 270|#00bd00;80 |#d7ffd7;60',
+ 'roundrect3' => '=axial 270|#00bd00;100 0|#ffffff;100 14|#ffffff;100 16|#00bd00;90 25|#b7ffb7;60 100',
+ 'roundrect4' => '=axial 0|#00bd00;100 0|#ffffff;100 20|#00bd00;50 30|#00bd00;90 80|#b7ffb7;60 100',
+ 'roundrect4ed' => '=path 48 48|#e7ffe7;20 0 70|#007900;20',
+ 'roundcurve2' => '=axial 270|#d7ffd7;60|#7777ff;80',
+ 'roundcurve1' => '=axial 270|#2222ff;80 |#d7ffd7;60',
+ 'roundcurve' => '=axial 270|#7777ff;80 |#d7ffd7;60',
+ 'roundpolyg' => '=radial -15 -20|#ffb7b7;50|#bd6622;90',
+ 'rpolyline' => '=axial 90|#ffff77;80 |#ff7700;60',
+ 'pushbtn1' => '=axial 0|#cccccc;100 0|#ffffff;100 10|#5a5a6a;100 80|#aaaadd;100 100',
+ 'pushbtn2' => '=axial 270|#ccccff;100 0|#ffffff;100 10|#5a5a7a;100 80|#bbbbee;100 100',
+ 'pushbtn3' => '=radial -15 -15|#ffffff;100 0|#333344;100 100',
+ 'pushbtn4' => '=axial 270|#ccccff;100 0|#ffffff;100 10|#7a7a9a;100 80|#bbbbee;100 100',
+ 'pushbtn_edge' => '=axial 140|#ffffff;100 0|#555566;100 100',
+ 'pushbtn_edge2' => '=axial 92|#ffffff;100 0|#555566;100 100',
+ 'logoshape' => '=axial 270|#ffffff|#7192aa',
+ 'logopoint' => '=radial -20 -20|#ffffff 0|#f70000 48|#900000 80|#ab0000 100',
+ 'logoptshad' => '=path 0 0|#770000;64 0|#770000;70 78|#770000;0 100',
+ );
+
+
+# contenu des pages exemples
+my %pagesconf = ('Rectangle' => {'consigne' => {-itemtype => 'text',
+ -coords => [-285, 155],
+ -params => {-font => $font_9b,
+ -text => "Mouse button 1 drag objects,\nEscape key reset transfos.",
+ -color => '#2222cc',
+ },
+ },
+ # roudedrectangle simple + radius 20
+ 'rr1' => {-itemtype => 'roundedrectangle',
+ -coords => [[-200, 30], [50, 130]],
+ -radius => 20,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect2',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['move'],
+ },
+ },
+
+ # roudedrectangle 'carré' (radius automatique)
+ 'rr2' => {-itemtype => 'roundedrectangle',
+ -coords => [[-250, -100], [-90, 60]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect1',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 30,
+ -tags => ['move'],
+ },
+ },
+ # cas particulier -> hippodrome (radius = h/2)
+ 'rr3' => {-itemtype => 'roundedrectangle',
+ -coords => [[-30, 80], [130, 160]],
+ -radius => 40,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect3',
+ -linewidth => 4,
+ -linecolor => '#000000',
+ -priority => 30,
+ -tags => ['move'],
+ },
+ },
+
+ # utilisation de l'option -corners (pétales de fleur)
+ 'rr4a' => {-itemtype => 'roundedrectangle',
+ -coords => [[-30, -60], [110, 10]],
+ -radius => 40,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect3',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 30,
+ -tags => ['move'],
+ },
+ -corners => [1, 0, 1, 0],
+ },
+ 'rr4b' => {-itemtype => 'roundedrectangle',
+ -coords => [[118, -68], [220, -132]],
+ -radius => 40,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect3',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 30,
+ -tags => ['move'],
+ },
+ -corners => [1, 0, 1, 0],
+ },
+ 'rr4c' => {-itemtype => 'roundedrectangle',
+ -coords => [[118, -60], [190, 30]],
+ -radius => 40,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect3',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 30,
+ -tags => ['move'],
+ },
+ -corners => [0, 1, 0, 1],
+ },
+ 'rr4d' => {-itemtype => 'roundedrectangle',
+ -coords => [[40, -152], [110, -68]],
+ -radius => 40,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect3',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 30,
+ -tags => ['move'],
+ },
+ -corners => [0, 1, 0, 1],
+ },
+
+ # groupe de 2 boutons avec bordure externe
+ 'gr8' => {-itemtype => 'group',
+ -coords => [0, 0],
+ -params => {-priority => 10,
+ -tags => ['move'],
+ -atomic => 1,
+ },
+ -items => {'edge' => {-itemtype => 'roundedrectangle',
+ -coords => [[174, -36],[266, 146]],
+ -radius => 26,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect4ed',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 20,
+ },
+ },
+ 'top' => {-itemtype => 'roundedrectangle',
+ -coords => [[180, -30], [260, 53]],
+ -parentgroup => 'gr8',
+ -radius => 20,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect4',
+ -linewidth => 2.5,
+ -linecolor => '#000000',
+ -priority => 30,
+ },
+ -corners => [1, 0, 0, 1],
+ },
+ 'topico' => {-itemtype => 'curve',
+ -parentgroup => 'gr8',
+ -coords => [[220, -10],[200, 30],[240, 30]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => '#ffff00;80',
+ -linewidth => 1,
+ -linecolor => '#007900;80',
+ -priority => 50,
+ },
+ },
+ 'bottom' => {-itemtype => 'roundedrectangle',
+ -parentgroup => 'gr8',
+ -coords => [[180, 57], [260, 140]],
+ -radius => 20,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundrect4',
+ -linewidth => 2.5,
+ -linecolor => '#000000',
+ -priority => 30,
+ },
+ -corners => [0, 1, 1, 0],
+ },
+ 'bottomico' => {-itemtype => 'curve',
+ -parentgroup => 'gr8',
+ -coords => [[220, 120],[240, 80],[200, 80]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => '#ffff00;80',
+ -linewidth => 1,
+ -linecolor => '#007900;80',
+ -priority => 50,
+ },
+ },
+ },
+ },
+ },
+ 'Hippodrome' => {'hp1edge' => {-itemtype => 'hippodrome',
+ -coords => [[-206, -126], [-114, 46]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 10,
+ },
+ },
+ 'hp1' => {-itemtype => 'hippodrome',
+ -coords => [[-200, -120], [-120, 40]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn1',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ },
+
+ 'hp2edge' => {-itemtype => 'hippodrome',
+ -coords => [[-86, -126], [6, 46]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 10,
+ },
+ },
+ 'hp2T' => {-itemtype => 'hippodrome',
+ -coords => [[-80, -120], [0, -68]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn1',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -orientation => 'vertical',
+ -trunc => 'bottom',
+ },
+ 'hp2C' => {-itemtype => 'hippodrome',
+ -coords => [[-80, -66.5], [0, -13.5]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn1',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -trunc => 'both',
+ },
+ 'hp2B' => {-itemtype => 'hippodrome',
+ -coords => [[-80, -12], [0, 40]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn1',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -orientation => 'vertical',
+ -trunc => 'top',
+ },
+
+ 'hp3edge' => {-itemtype => 'hippodrome',
+ -coords => [[-204, 96], [204, 144]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge2',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 10,
+ },
+ },
+ 'hp3G' => {-itemtype => 'hippodrome',
+ -coords => [[-200, 100], [-120, 140]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn2',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -trunc => 'right',
+ },
+ 'hp3Gico' => {-itemtype => 'curve',
+ -coords => [[-180, 120],[-164, 128],[-164, 112]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => '#000000',
+ -linewidth => 1,
+ -linecolor => '#aaaaaa',
+ -relief => 'raised',
+ -priority => 30,
+ },
+ -contours => [['add',-1,[[-160, 120],[-144, 128],[-144, 112]]]],
+ },
+
+ 'hp3C1' => {-itemtype => 'hippodrome',
+ -coords => [[-118, 100], [-41, 140]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn2',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -trunc => 'both',
+ },
+ 'hp3C1ico' => {-itemtype => 'curve',
+ -coords => [[-88, 120],[-72, 128],[-72, 112]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => '#000000',
+ -linewidth => 1,
+ -linecolor => '#aaaaaa',
+ -priority => 30,
+ -relief => 'raised',
+ },
+ },
+
+ 'hp3C2' => {-itemtype => 'hippodrome',
+ -coords => [[-39, 100], [39, 140]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn2',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -trunc => 'both',
+ },
+ 'hp3C2ico' => {-itemtype => 'rectangle',
+ -coords => [[-6, 114],[6, 126]],
+ -params => {-filled => 1,
+ -fillcolor => '#000000',
+ -linewidth => 1,
+ -linecolor => '#aaaaaa',
+ -priority => 30,
+ },
+ },
+ 'hp3C3' => {-itemtype => 'hippodrome',
+ -coords => [[41, 100], [118, 140]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn2',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -trunc => 'both',
+ },
+ 'hp3C3ico' => {-itemtype => 'curve',
+ -coords => [[88, 120],[72, 112],[72, 128]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => '#000000',
+ -linewidth => 1,
+ -linecolor => '#aaaaaa',
+ -priority => 30,
+ -relief => 'raised',
+ },
+ },
+
+ 'hp3D' => {-itemtype => 'hippodrome',
+ -coords => [[120, 100], [200, 140]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn2',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -trunc => 'left',
+ },
+ 'hp3Dico' => {-itemtype => 'curve',
+ -coords => [[180, 120],[164, 112],[164, 128]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => '#000000',
+ -linewidth => 1,
+ -linecolor => '#aaaaaa',
+ -priority => 30,
+ -relief => 'raised',
+ },
+ -contours => [['add',-1,[[160, 120],[144, 112],[144, 128]]]],
+ },
+
+ 'hp4aedge' => {-itemtype => 'hippodrome',
+ -coords => [[27, -126], [85, -68]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 20,
+ },
+ },
+ 'hp4a' => {-itemtype => 'hippodrome',
+ -coords => [[32, -121], [80, -73]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn3',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 30,
+ },
+ },
+ 'hp4bedge' => {-itemtype => 'hippodrome',
+ -coords => [[94, -126], [206, -14]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 20,
+ },
+ },
+ 'hp4b' => {-itemtype => 'hippodrome',
+ -coords => [[102, -118], [198, -22]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn3',
+ -linewidth => 3,
+ -linecolor => '#000000',
+ -priority => 30,
+ },
+ },
+ 'hp5aedge' => {-itemtype => 'hippodrome',
+ -coords => [[-19, -34], [19, 34]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 10,
+ },
+ -rotate => 30,
+ -translate => [45, 30],
+ },
+ 'hp5a' => {-itemtype => 'hippodrome',
+ -coords => [[-15, -30], [15, 30]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn1',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -rotate => 30,
+ -translate => [45, 30],
+ },
+ 'hp5bedge' => {-itemtype => 'hippodrome',
+ -coords => [[-19, -34], [19, 34]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 10,
+ },
+ -rotate => 30,
+ -translate => [90, 30],
+ },
+ 'hp5b' => {-itemtype => 'hippodrome',
+ -coords => [[-15, -30], [15, 30]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn1',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -rotate => 30,
+ -translate => [90, 30],
+ },
+
+ 'hp5cedge' => {-itemtype => 'hippodrome',
+ -coords => [[-19, -34], [19, 34]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 10,
+ },
+ -rotate => 30,
+ -translate => [135, 30],
+ },
+ 'hp5c' => {-itemtype => 'hippodrome',
+ -coords => [[-15, -30], [15, 30]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn1',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -rotate => 30,
+ -translate => [135, 30],
+ },
+
+ 'hp5dedge' => {-itemtype => 'hippodrome',
+ -coords => [[-19, -34], [19, 34]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn_edge',
+ -linewidth => 1,
+ -linecolor => '#ffffff',
+ -priority => 10,
+ },
+ -rotate => 30,
+ -translate => [180, 30],
+ },
+ 'hp5d' => {-itemtype => 'hippodrome',
+ -coords => [[-15, -30], [15, 30]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn1',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 20,
+ },
+ -rotate => 30,
+ -translate => [180, 30],
+ },
+
+ },
+
+ 'Polygone' => {'triangle' => {-itemtype => 'polygone',
+ -coords => [-210, -90],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundpolyg',
+ -linewidth => 2,
+ -linecolor => '#330000',
+ -priority => 20,
+ },
+ -numsides => 3,
+ -radius => 78,
+ -corner_radius => 10,
+ -startangle => 90,
+ },
+ 'text1' => {-itemtype => 'text',
+ -coords => [-210, -90],
+ -params => {-font => $font_9b,
+ -text => "Triangle",
+ -anchor => 'center',
+ -alignment => 'center',
+ -color => '#660000',
+ -priority => 50,
+ },
+ },
+ 'carre' => {-itemtype => 'polygone',
+ -coords => [-80, -70],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundpolyg',
+ -linewidth => 2,
+ -linecolor => '#330000',
+ -priority => 20,
+ },
+ -numsides => 4,
+ -radius => 70,
+ -corner_radius => 10,
+ -startangle => 90,
+ },
+
+ 'text2' => {-itemtype => 'text',
+ -coords => [-80, -70],
+ -params => {-font => $font_9b,
+ -text => 'Carré',
+ -anchor => 'center',
+ -alignment => 'center',
+ -color => '#660000',
+ -priority => 50,
+ },
+ },
+ 'pentagone' => {-itemtype => 'polygone',
+ -coords => [65, -70],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundpolyg',
+ -linewidth => 2,
+ -linecolor => '#330000',
+ -priority => 20,
+ },
+ -numsides => 5,
+ -radius => 70,
+ -corner_radius => 10,
+ -startangle => 270,
+ },
+
+ 'text3' => {-itemtype => 'text',
+ -coords => [65, -70],
+ -params => {-font => $font_9b,
+ -text => 'Pentagone',
+ -anchor => 'center',
+ -alignment => 'center',
+ -color => '#660000',
+ -priority => 50,
+ },
+ },
+
+ 'hexagone' => {-itemtype => 'polygone',
+ -coords => [210, -70],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundpolyg',
+ -linewidth => 2,
+ -linecolor => '#330000',
+ -priority => 20,
+ },
+ -numsides => 6,
+ -radius => 68,
+ -corner_radius => 10,
+ },
+ 'text4' => {-itemtype => 'text',
+ -coords => [210, -70],
+ -params => {-font => $font_9b,
+ -text => 'Hexagone',
+ -anchor => 'center',
+ -alignment => 'center',
+ -color => '#660000',
+ -priority => 50,
+ },
+ },
+ 'heptagone' => {-itemtype => 'polygone',
+ -coords => [-215, 90],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundpolyg',
+ -linewidth => 2,
+ -linecolor => '#330000',
+ -priority => 20,
+ },
+ -numsides => 7,
+ -radius => 64,
+ -corner_radius => 10,
+ },
+ 'text5' => {-itemtype => 'text',
+ -coords => [-215, 90],
+ -params => {-font => $font_9b,
+ -text => 'Heptagone',
+ -anchor => 'center',
+ -alignment => 'center',
+ -color => '#660000',
+ -priority => 50,
+ },
+ },
+ 'octogone' => {-itemtype => 'polygone',
+ -coords => [-76, 90],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundpolyg',
+ -linewidth => 2,
+ -linecolor => '#330000',
+ -priority => 20,
+ },
+ -numsides => 8,
+ -radius => 64,
+ -corner_radius => 10,
+ },
+ 'text6' => {-itemtype => 'text',
+ -coords => [-76, 90],
+ -params => {-font => $font_9b,
+ -text => 'Octogone',
+ -anchor => 'center',
+ -alignment => 'center',
+ -color => '#660000',
+ -priority => 50,
+ },
+ },
+
+ 'petagone' => {-itemtype => 'polygone',
+ -coords => [66, 90],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundpolyg',
+ -linewidth => 2,
+ -linecolor => '#330000',
+ -priority => 20,
+ },
+ -numsides => 32,
+ -radius => 64,
+ -corner_radius => 10,
+ },
+ 'text7' => {-itemtype => 'text',
+ -coords => [66, 90],
+ -params => {-font => $font_9b,
+ -text => '32 cotés...',
+ -anchor => 'center',
+ -alignment => 'center',
+ -color => '#660000',
+ -priority => 50,
+ },
+ },
+ 'etoile' => {-itemtype => 'polygone',
+ -coords => [210, 90],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundpolyg',
+ -linewidth => 2,
+ -linecolor => '#330000',
+ -priority => 20,
+ },
+ -numsides => 5,
+ -radius => 92,
+ -inner_radius => 36,
+ -corner_radius => 10,
+ -startangle => 270,
+ -corners => [1,0,1,0,1,0,1,0,1,0],
+ },
+ 'text8' => {-itemtype => 'text',
+ -coords => [210, 90],
+ -params => {-font => $font_9b,
+ -text => 'Etoile',
+ -anchor => 'center',
+ -alignment => 'center',
+ -color => '#660000',
+ -priority => 50,
+ },
+ },
+ },
+
+ 'Polyline' => {'consigne' => {-itemtype => 'text',
+ -coords => [-285, 155],
+ -params => {-font => $font_9b,
+ -text => "Mouse button 1 drag objects,\nEscape key reset transfos.",
+ -color => '#2222cc',
+ },
+ },
+ 'a' => {-itemtype => 'polyline',
+ -coords => [[-200, -115],[-200, -100],[-218, -115],[-280, -115],[-280, -16],
+ [-218, -16],[-200, -31],[-200, -17.5],[-150, -17.5],[-150,-115]],
+ -corners_radius => [0, 0, 42, 47, 47, 42, 0, 0, 0, 0, 0, 0],
+ -params => {-closed => 1,
+ -filled => 1,
+ -visible => 1,
+ -fillcolor => 'rpolyline',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 50,
+ -tags => ['move'],
+ },
+ -contours => [['add', -1, [[-230, -80],[-230, -50],[-200, -50],[-200, -80]],15]],
+ },
+ 'b' => {-itemtype => 'polyline',
+ -coords => [[-138, -150],[-138, -17.5],[-88, -17.5],[-88, -31],[-70, -16],
+ [-8, -16],[-8, -115],[-70, -115],[-88, -100],[-88, -150]],
+ -corners_radius => [0, 0, 0, 0, 42, 47, 47, 42, 0, 0, 0, 0, 0, 0],
+ -params => {-closed => 1,
+ -filled => 1,
+ -visible => 1,
+ -fillcolor => 'rpolyline',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 50,
+ -tags => ['move'],
+ },
+ -contours => [['add', -1, [[-88, -80],[-88, -50],[-58, -50],[-58, -80]],15]],
+ },
+ 'c' => {-itemtype => 'polyline',
+ -coords => [[80, -76],[80, -110],[60, -115],[0, -115],[0, -16],
+ [60, -16],[80, -21],[80, -57],[50, -47],[50, -86]],
+ -corners_radius => [0, 0, 70, 47, 47, 70, 0, 0, 14, 14, 0, 0, 0,0 ],
+ -params => {-closed => 1,
+ -filled => 1,
+ -visible => 1,
+ -fillcolor => 'rpolyline',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 50,
+ -tags => ['move'],
+ },
+ },
+ 'spirale' => {-itemtype => 'polyline',
+ -coords => [[215, -144],[139, -144],[139, 0],[268, 0],[268, -116],
+ [162.5, -116],[162.5, -21],[248, -21],[248, -96],[183, -96],
+ [183, -40],[231,-40],[231, -80],[199, -80],[199, -55],[215, -55]],
+ -corners_radius => [0, 76, 68, 61, 55, 50, 45, 40, 35, 30, 26, 22, 18, 14, 11],
+ -params => {-closed => 1,
+ -filled => 1,
+ -visible => 1,
+ -fillcolor => 'rpolyline',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 50,
+ -tags => ['move'],
+ },
+ },
+ 'logo' => {-itemtype => 'group',
+ -coords => [0, 0],
+ -params => {-priority => 30,
+ -atomic => 1,
+ -tags => ['move'],
+ },
+ -items => {'tkzinc' => {-itemtype => 'polyline',
+ -coords => [[-150,10],[-44,10],[-44,68],[-28,51],[6,51],
+ [-19,79],[3,109],[53,51],[5,51],[5,10],[140,10],
+ [52,115],[96,115],[96,47],[196,47],[196,158],
+ [155,158],[155,89],[139,89],[139,160],[101, 160],
+ [101,132],[85,132],[85,160],[-42,160],[-2,115],
+ [-30,115],[-46,91],[-46,115],[-76,115],[-76,51],
+ [-98,51],[-98,115],[-130,115],[-130,51],[-150, 51]],
+ -corners_radius => [0,0,0,0,0,0,0,0,0,0,30,0,0,50,50,
+ 0,0,8,8,0,0,8,8,0,27],
+ -params => {-closed => 1,
+ -filled => 1,
+ -visible => 1,
+ -fillcolor => 'logoshape',
+ -linewidth => 2.5,
+ -linecolor => '#000000',
+ -priority => 10,
+ -fillrule => 'nonzero',
+ },
+ -contours => [['add',1,[[245,88],[245,47],[190,47],[190,158],
+ [259,158],[259,117],[230,117],[230,88]],
+ 0, undef, [0,0,55,55,0,0,15,15]]],
+ },
+ 'shad' => {-itemtype => 'arc',
+ -coords => [[75, 91],[115,131]],
+ -params => {-priority => 20,
+ -filled => 1,
+ -linewidth => 0,
+ -fillcolor => 'logoptshad',
+ -closed => 1,
+ },
+ },
+ 'point' => {-itemtype => 'arc',
+ -coords => [[70, 86],[110,126]],
+ -params => {-priority => 50,
+ -filled => 1,
+ -linewidth => 1,
+ -linecolor => '#a10000',
+ -fillcolor => 'logopoint',
+ -closed => 1,
+ },
+ },
+ },
+ },
+ },
+
+ 'MultiContours' => {'consigne' => {-itemtype => 'text',
+ -coords => [-285, 155],
+ -params => {-font => $font_9b,
+ -text => "Mouse button 1 drag objects,\nEscape key reset transfos.",
+ -color => '#2222cc',
+ },
+ },
+ 'mc1' => {-itemtype => 'roundedcurve',
+ -coords => [[-30, -170], [-130, 0],[70, 0]],
+ -radius => 14,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundcurve2',
+ -linewidth => 1,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['move'],
+ -fillrule => 'odd',
+ },
+ -contours => [['add',1,[[-30,-138],[-100,-18],[40,-18]],8],
+ ['add',1,[[-30,-130],[ -92,-22],[32,-22]],5],
+ ['add',1,[[-30,-100],[ -68,-36],[8,-36]],5],
+ ['add',1,[[-30, -92],[ -60,-40],[0,-40]],3],],
+ },
+ 'mc2' => {-itemtype => 'polyline',
+ -coords => [[-250,-80], [-240,-10],[-285,-10],[-285,80],
+ [-250, 80],[-250, 40],[-170, 40],[-170,80],
+ [-100,80],[-100,40],[-20,40],[-20,80],[30,80],
+ [-10, 0],[-74, -10],[-110, -80]],
+ -corners_radius => [24,4, 40, 20, 0, 40, 40, 0, 0, 40, 40, 0, 30, 75, 0, 104],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundcurve1',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 30,
+ -tags => ['move'],
+ },
+ -contours => [['add',-1,[[-240,-72],[-230,0],[-169,0],[-185, -72]],
+ 0, undef, [16, 16, 0, 0]],
+ ['add', -1, [[-175,-72],[-159,0],[-78,0],[-116, -72]],
+ 0, undef, [0, 0, 8, 88]],
+ ['add', 1, [[-245,45],[-245,115],[-175,115],[-175, 45]],
+ 35],
+ ['add', -1, [[-225,65],[-225,95],[-195,95],[-195, 65]],
+ 15],
+ ['add', 1, [[-95,45],[-95,115],[-25,115],[-25, 45]],
+ 35],
+ ['add', -1, [[-75,65],[-75,95],[-45,95],[-45, 65]],
+ 15],
+ ],
+ },
+ 'mc3' => {-itemtype => 'roundedcurve',
+ -coords => [[-10, 170], [256, 170],[312, 60],[48, 60]],
+ -radius => 34,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundcurve2',
+ -linewidth => 2.5,
+ -linecolor => '#000000',
+ -priority => 40,
+ -tags => ['move'],
+ },
+ -contours => [['add', -1, [[58, 62],[12, 144],[60, 172],[104, 88]],27],
+ ['add', 1, [[48, 77],[48, 119],[90, 119],[90, 77]],21],
+ ['add', -1, [[244, 58],[198, 140],[246, 168],[290, 84]],27],
+ ['add', 1, [[213, 110],[213, 152],[255, 152],[255, 110]],21],
+ ['add', -1, [[150, 60],[150, 170],[160, 170],[160, 60]],0]],
+ },
+ 'mc4' => {-itemtype => 'roundedcurve',
+ -coords => [[222, -150],[138, -150],[180, -50],[138, -150],
+ [80, -92],[180, -50],[80, -92],[80, -8],
+ [180, -50],[80, -8],[138, 50],[180, -50],
+ [138, 50],[222, 50],[179.8, -50],[222, 50],
+ [280, -8],[180, -50],[280, -8],[280, -92],
+ [180, -50],[280, -92],[222,-150],[180,-50]],
+ -radius => 28,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'roundcurve',
+ -linewidth => 2,
+ -linecolor => '#000000',
+ -priority => 30,
+ -tags => ['move'],
+ },
+ -contours => [['add', -1, [[160, -70],[160, -30],[200, -30],[200, -70]],20]],
+ },
+ },
+
+ 'TabBox' => {'bo1' => {-itemtype => 'tabbox',
+ -coords => [[-240, -140], [240, 120]],
+ -radius => 8,
+ -tabwidth => 72,
+ -tabheight => 28,
+ -numpages => 8,
+ -anchor => 'n',
+ -alignment => 'left',
+ -overlap => 3,
+ -tabtitles => ['A', 'B', 'C', 'D', 'E', 'F','G','H'],
+ -params => {-closed => 1,
+ -priority => 100,
+ -filled => 1,
+ -fillcolor => '#ffffff',
+ -linewidth => 1.2,
+ -linecolor => '#000000',
+ -tags => ['div2', 'divider', 'intercalaire'],
+ },
+ },
+
+ 'back' => {-itemtype => 'roundedrectangle',
+ -coords => [[-242, -142], [242, 122]],
+ -radius => 10,
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => '#777777;80',
+ -linewidth => 1,
+ -linecolor => '#777777;80',
+ },
+ },
+
+ 'anchor' => {-itemtype => 'text',
+ -coords => [-120, 138],
+ -params => {-text => 'tabs anchor',
+ -color => '#2222cc',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ },
+ },
+
+ 'anchorN' => {-itemtype => 'hippodrome',
+ -coords => [[-210, 150], [-165, 176]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn4',
+ -linewidth => 1.5,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['sel1','n','btn','selector'],
+ },
+ -trunc => 'right',
+ },
+ 'txtanN' => {-itemtype => 'text',
+ -coords => [-187, 163],
+ -params => {-text => 'N',
+ -color => '#000000',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ -tags => ['sel1','n','btntext','selector'],
+ },
+ },
+
+ 'anchorE' => {-itemtype => 'hippodrome',
+ -coords => [[-163, 150], [-120, 176]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn4',
+ -linewidth => 1.5,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['sel1','e','btn','selector'],
+ },
+ -trunc => 'both',
+ },
+ 'txtanE' => {-itemtype => 'text',
+ -coords => [-141.5, 163],
+ -params => {-text => 'E',
+ -color => '#000000',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ -tags => ['sel1','e','btntext','selector'],
+ },
+ },
+
+ 'anchorS' => {-itemtype => 'hippodrome',
+ -coords => [[-118, 150], [-75, 176]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn4',
+ -linewidth => 1.5,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['sel1','s','btn','selector'],
+ },
+ -trunc => 'both',
+ },
+ 'txtanS' => {-itemtype => 'text',
+ -coords => [-96.5, 163],
+ -params => {-text => 'S',
+ -color => '#000000',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ -tags => ['sel1','s','btntext','selector'],
+ },
+ },
+ 'anchorW' => {-itemtype => 'hippodrome',
+ -coords => [[-73, 150], [-28, 176]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn4',
+ -linewidth => 1.5,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['sel1','w','btn','selector'],
+ },
+ -trunc => 'left',
+ },
+ 'txtanW' => {-itemtype => 'text',
+ -coords => [-52, 163],
+ -params => {-text => 'W',
+ -color => '#000000',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ -tags => ['sel1','w','btntext','selector'],
+ },
+ },
+ 'alignment' => {-itemtype => 'text',
+ -coords => [120, 138],
+ -params => {-text => 'tabs alignment',
+ -color => '#2222cc',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ },
+ },
+ 'alignG' => {-itemtype => 'hippodrome',
+ -coords => [[30, 150], [90, 176]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn4',
+ -linewidth => 1.5,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['sel2','left','btn','selector'],
+ },
+ -trunc => 'right',
+ },
+ 'txtalG' => {-itemtype => 'text',
+ -coords => [60, 163],
+ -params => {-text => 'left',
+ -color => '#000000',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ -tags => ['sel2','left','btntext','selector'],
+ },
+ },
+ 'alignC' => {-itemtype => 'hippodrome',
+ -coords => [[92, 150], [148, 176]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn4',
+ -linewidth => 1.5,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['sel2','center','btn','selector'],
+ },
+ -trunc => 'both',
+ },
+ 'txtalC' => {-itemtype => 'text',
+ -coords => [120, 163],
+ -params => {-text => 'center',
+ -color => '#000000',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ -tags => ['sel2','center','btntext','selector'],
+ },
+ },
+ 'alignD' => {-itemtype => 'hippodrome',
+ -coords => [[150, 150], [210, 176]],
+ -params => {-closed => 1,
+ -filled => 1,
+ -fillcolor => 'pushbtn4',
+ -linewidth => 1.5,
+ -linecolor => '#000000',
+ -priority => 20,
+ -tags => ['sel2','right','btn','selector'],
+ },
+ -trunc => 'left',
+ },
+ 'txtalD' => {-itemtype => 'text',
+ -coords => [180, 163],
+ -params => {-text => 'right',
+ -color => '#000000',
+ -font => $font_9b,
+ -anchor => 'center',
+ -alignment => 'center',
+ -priority => 40,
+ -tags => ['sel2','right','btntext','selector'],
+ },
+ },
+ },
+
+ 'PathLine' => {'consigne' => {-itemtype => 'text',
+ -coords => [-285, 155],
+ -params => {-font => $font_9b,
+ -text => "Mouse button 1 drag objects,\nEscape key reset transfos.",
+ -color => '#2222cc',
+ },
+ },
+ 'pl1' => {-itemtype => 'pathline',
+ -metacoords => {-type => 'polygone',
+ -coords => [0, 0],
+ -numsides => 12,
+ -radius => 200,
+ -inner_radius => 100,
+ -startangle => -8,
+ },
+ -linewidth => 20,
+ -closed => 1,
+ -graduate => {-type => 'linear',
+ -colors => ['#ff0000', '#ff00ff', '#0000ff', '#00ffff',
+ '#00ff00', '#ffff00', '#ff0000'],
+ },
+ -params => {-priority => 100,
+ -tags => ['move'],
+ },
+ },
+
+ 'pl2' => {-itemtype => 'group',
+ -coords => [0, 0],
+ -params => {-priority => 200,
+ -atomic => 1,
+ -tags => ['move'],
+ },
+ -items => {'in' => {-itemtype => 'pathline',
+ -coords => [[30, -60],[-30, -60],[-30, -30],
+ [-60, -30],[-60, 30],[-30, 30],
+ [-30, 60],[30, 60],[30, 30],
+ [60, 30],[60, -30],[30, -30]],
+ -linewidth => 16,
+ -closed => 1,
+ -shifting => 'left',
+ -graduate => {-type => 'transversal',
+ -colors => ['#00aa77;100', '#00aa77;0'],
+ },
+ -params => {-priority => 10,
+ },
+ },
+
+ 'out' => {-itemtype => 'pathline',
+ -coords => [[30, -60],[-30, -60],[-30, -30],
+ [-60, -30],[-60, 30],[-30, 30],
+ [-30, 60],[30, 60],[30, 30],
+ [60, 30],[60, -30],[30, -30]],
+ -linewidth => 10,
+ -closed => 1,
+ -shifting => 'right',
+ -graduate => {-type => 'transversal',
+ -colors => ['#00aa77;100', '#00aa77;0'],
+ },
+ -params => {-priority => 10,
+ },
+ },
+ },
+ },
+
+ 'pl3' => {-itemtype => 'group',
+ -coords => [0, 0],
+ -params => {-priority => 100,
+ -atomic => 1,
+ -tags => ['move'],
+ },
+ -items => {'back' => {-itemtype => 'arc',
+ -coords => [[-150, -150],[150,150]],
+ -params => {-priority => 10,
+ -closed => 1,
+ -filled => 1,
+ -fillcolor => '=radial 15 15|#ffffff;40|#aaaaff;10',
+ -linewidth => 0,
+ },
+ },
+ 'light' => {-itemtype => 'pathline',
+ -metacoords => {-type => 'polygone',
+ -coords => [0, 0],
+ -numsides => 30,
+ -radius => 150,
+ -startangle => 240,
+ },
+ -linewidth => 20,
+ -shifting => 'right',
+ -closed => 1,
+ -graduate => {-type => 'double',
+ -colors => [['#ffffff;0', '#222299;0', '#ffffff;0'],
+ ['#ffffff;100', '#222299;70', '#ffffff;100']],
+ },
+ -params => {-priority => 50,
+ },
+ },
+ 'bord' => {-itemtype => 'arc',
+ -coords => [[-150, -150],[150,150]],
+ -params => {-priority => 100,
+ -closed => 1,
+ -filled => 0,
+ -linewidth => 2,
+ -linecolor => '#000033;80'
+ },
+ },
+
+ },
+ },
+ },
+
+ );
+
+my %tabtable = ('n' => {-numpages => 8,
+ -titles => ['A','B','C','D','E','F','G','H'],
+ -names => ['Avant Garde','Bodini','Clarendon','Didot',
+ 'Eras','Frutiger','Garamond','Helvetica'],
+ -images => ['avantgarde.gif','bodini.gif','clarendon.gif','didot.gif',
+ 'eras.gif','frutiger.gif','garamond.gif','helvetica.gif'],
+ },
+ 'e' => {-numpages => 5,
+ -titles => ['I','J','K','L','M'],
+ -names => ['Impact','Jenson','Kabel','Lubalin Graph','Matura'],
+ -images => ['impact.gif','jenson.gif','kabel.gif','lubalingraph.gif','matura.gif'],
+ },
+ 's' => {-numpages => 8,
+ -titles => ['N','O','P','Q','R','S','T','U'],
+ -names => ['New Century Scoolbook','Optima','Peignot','Quorum',
+ 'Revue','Souvenir','Times New Roman', 'Univers'],
+ -images => ['newcenturyscoolbook.gif','optima.gif','peignot.gif','quorum.gif',
+ 'revue.gif','souvenir.gif','timesnewroman.gif','univers.gif'],
+ },
+ 'w' => {-numpages => 5,
+ -titles => ['V','W','X','Y','Z'],
+ -names => ['Veljovic','Warnock','X-files','Yellow Submarine','zapf Chancery'],
+ -images => ['veljovic.gif','warnock.gif','xfiles.gif','yellowsubmarine.gif','zapfchancery.gif'],
+ },
+ );
+
+
+
+# creation de la fenetre principale
+my $mw = MainWindow->new();
+$mw->geometry("700x560+0+0");
+$mw->title('Test Graphics Module');
+
+
+# creation du widget Zinc
+my $zinc = $mw->Zinc(-render => 1,
+ -width => 700,
+ -height => 560,
+ -borderwidth => 0,
+ -lightangle => 140,
+ -borderwidth => 0,
+ -backcolor => '#cccccc',);
+$zinc->pack(-fill => 'both', -expand => 1);
+
+
+# initialise les gradiants nommés
+&setGradiants($zinc, \%gradset);
+
+# initialise les images de fontes
+while (my ($anchor, $table) = each(%tabtable)) {
+ my @images;
+ foreach my $filename (@{$table->{'-images'}}) {
+ my $image = $zinc->Photo(-file => Tk::findINC($filename));
+ push(@images, $image);
+ }
+ $tabtable{$anchor}->{'-images'} = \@images;
+}
+
+# création de la vue principale
+my $tgroup = $zinc->add('group', 1);
+$zinc->coords($tgroup, [350, 240]);
+
+# consigne globale
+$zinc->add('text', 1,
+ -position => [50, 470],
+ -text => "Global interations :\n<Up>, <Down>, <Left> and <Right> keys move content of TabBox pages\n<Plus> and <Minus> keys zoom out and zoom in this page\n<Greater> and <Less> keys rotate this page\n<Escape> key reset transfos",
+ -font => $font_9b,
+ -color => '#555555',
+ -spacing => 2,
+ );
+
+# Création des pages d'exemples
+my ($shapes, $tcoords) = &TabBoxCoords([[-315, -210],[315, 210]],
+ -numpages => 7,
+ -overlap => 2,
+ -radius => 8,
+ -tabheight => 26,
+ -tabwidth => [92,100,82,82,82,120,80],
+ );
+
+# to find some images (used as textures) needed by this demo
+push @INC , Tk->findINC('demos/zinc_data');
+#print "image_path=$image_path\n";
+my $texture = $zinc->Photo(-file => Tk::findINC('paper.gif'));
+
+# création des items zinc correspondants
+my $i = scalar(@{$shapes}) - 1;
+my @pagenames = ('Rectangle', 'Hippodrome', 'Polygone', 'Polyline', 'PathLine', 'MultiContours', 'TabBox');
+my @pagegroups;
+foreach my $shape (reverse @{$shapes}) {
+ my $divgroup = $zinc->add('group', $tgroup);
+
+ # création de l'intercalaire
+ my $divider = $zinc->add('curve', $divgroup,
+ $shape,
+ -closed => 1,
+ -priority => 10,
+ -linewidth => 1,
+ -linecolor => '#000000',
+ -filled => 1,
+ -tile => $texture,
+ -tags => ['div1', $i, 'divider', 'intercalaire'],
+ );
+
+ # groupe page clippé
+ my $page = $zinc->add('group', $divgroup,
+ -priority => 100,
+ -tags => ['div1', $i, 'page'],
+ );
+ my $clip = $zinc->add('rectangle', $page,
+ [[-300, -170],[300, 195]],
+ -linewidth => 1,
+ -linecolor => '#000099',
+ -filled => 1,
+ -fillcolor => '#000000;4',
+ );
+ $zinc->itemconfigure($page, -clip => $clip);
+
+ my $pgroup = $zinc->add('group', $page,
+ -tags => ['div1', $i, 'content'],
+ );
+
+ push(@pagegroups, $pgroup);
+
+ # titre de l'intercalaire
+ $zinc->add('text', $divgroup,
+ -position => $tcoords->[$i],
+ -text => $pagenames[$i],
+ -font => $font_9b,
+ -alignment => 'center',
+ -anchor => 'center',
+ -color => '#000099',
+ -priority => 200,
+ -tags => ['div1', $i, 'divider','titre'],
+ );
+ $i--;
+}
+
+# création du contenu des pages
+my $i = 0;
+foreach my $pagename (reverse @pagenames) {
+ my $pagestyle = $pagesconf{$pagename};
+ if ($pagestyle) {
+ my $pgroup = $pagegroups[$i];
+ while (my ($itemname, $itemstyle) = each(%{$pagestyle})) {
+ if ($itemstyle->{'-itemtype'} eq 'tabbox') {
+ &buildTabBox($zinc, $pgroup, $itemstyle, $itemname);
+
+ } else {
+ if ($itemstyle->{'-itemtype'} eq 'group') {
+ my $subgroup = &buildZincItem($zinc, $pgroup, $itemstyle, undef, $itemname);
+ while (my ($name, $style) = each(%{$itemstyle->{'-items'}})) {
+ &buildZincItem($zinc, $subgroup, $style, undef, $name);
+ }
+ } else {
+ my $group = ($itemname eq 'consigne') ? $zinc->group($pgroup) : $pgroup;
+ &buildZincItem($zinc, $group, $itemstyle, undef, $itemname);
+ }
+ }
+ }
+ }
+ $i++;
+}
+
+&clickSelector('sel1','n');
+&clickSelector('sel2','left');
+&selectDivider('div1', 0);
+
+&setBindings;
+
+
+MainLoop;
+#----------------------------------------------------------------------- fin de MAIN
+
+
+sub setBindings {
+ # grab keyboard
+ $mw->Tk::focus();
+
+ # plus,moins : Zoom++, Zoom--
+ $mw->Tk::bind('<plus>', sub {viewZoom('up');});
+ $mw->Tk::bind('<minus>', sub {viewZoom('down');});
+
+ # Up, Down, Right, Left : Translate
+ $mw->Tk::bind('<KeyPress-Up>', sub {viewTranslate('up');});
+ $mw->Tk::bind('<KeyPress-Down>', sub {viewTranslate('down');});
+ $mw->Tk::bind('<KeyPress-Left>', sub {viewTranslate('left');});
+ $mw->Tk::bind('<KeyPress-Right>', sub {viewTranslate('right');});
+
+
+ # >, < : Rotate counterclockwise et clockwise
+ $mw->Tk::bind('<greater>', sub {viewRotate('cw');});
+ $mw->Tk::bind('<less>', sub {viewRotate('ccw');});
+
+ # Escape : reset transfos
+ $mw->Tk::bind('<Escape>', sub {$zinc->treset('move');
+ $zinc->raise('move');
+ $zinc->treset($curview);});
+
+ $zinc->bind('divider', '<1>', sub {&selectDivider();});
+
+ $zinc->bind('selector', '<1>', sub {&clickSelector();});
+
+ $zinc->bind('move', '<1>', sub {&mobileStart();});
+ $zinc->bind('move', '<B1-Motion>', sub {&mobileMove();});
+ $zinc->bind('move', '<ButtonRelease>', sub {&mobileStop();});
+}
+
+
+sub selectDivider {
+ my ($divname, $numpage) = @_;
+ if (!defined $divname) {
+ my @tags = $zinc->itemcget('current', -tags);
+ $divname = $tags[0];
+ $numpage = $tags[1];
+ }
+
+ $zinc->itemconfigure("($divname && titre)", -color => '#000099');
+ $zinc->itemconfigure("($divname && intercalaire)", -linewidth => 1.4);
+ $zinc->itemconfigure("($divname && page)", -visible => 0);
+
+ my $divgroup = $zinc->group("($divname && $numpage)");
+ $zinc->raise($divgroup);
+ $curview = "($divname && $numpage && content)";
+ $zinc->itemconfigure("($divname && $numpage && titre)", -color => '#000000');
+ $zinc->itemconfigure("($divname && $numpage && intercalaire)", -linewidth => 2);
+ $zinc->itemconfigure("($divname && $numpage && page)", -visible => 1);
+
+ if ($divname eq 'div2') {
+ my $fontname = $tabtable{$tabanchor}->{'-names'}->[$numpage];
+ my $fontimage = $tabtable{$tabanchor}->{'-images'}->[$numpage];
+ $zinc->itemconfigure("($divname && fontname)", -text => $fontname);
+ $zinc->raise("($divname && fontname)");
+ $zinc->itemconfigure("($divname && fontimage)", -image => $fontimage);
+ $zinc->raise("($divname && fontimage)");
+ }
+}
+
+
+sub clickSelector {
+ my ($btngroup, $value) = @_;
+
+ if (!defined $btngroup and !defined $value) {
+ my @tags = $zinc->itemcget('current', -tags);
+ $btngroup = $tags[0];
+ $value = $tags[1];
+ }
+
+ $zinc->treset($btngroup);
+ $zinc->itemconfigure("($btngroup && btntext)", -color => '#444444');
+ $zinc->itemconfigure("($btngroup && $value && btntext)", -color => '#2222bb');
+ $zinc->translate("($btngroup && $value)", 0, 1);
+
+ if ($value eq 'n' or $value eq 'e' or $value eq 's' or $value eq 'w') {
+ $tabanchor = $value;
+
+ } elsif ($value eq 'left' or $value eq 'center' or $value eq 'right') {
+ $tabalign = $value;
+ }
+
+ my $tabtable = $tabtable{$tabanchor};
+ my $numpages = $tabtable->{'-numpages'};
+ my %tabparams = (-radius => 8,
+ -tabwidth => 72,
+ -tabheight => 28,
+ -numpages => $numpages,
+ -anchor => $tabanchor,
+ -alignment => $tabalign,
+ -overlap => 3,
+ );
+
+ my ($shapes, $tcoords) = &TabBoxCoords([[-240, -140], [240, 120]], %tabparams);
+
+ for (my $index = 7; $index >= 0; $index--) {
+ my $divgroup = $zinc->group("(div2 && $index && intercalaire)");
+ $zinc->itemconfigure($divgroup, -visible => ($index < $numpages));
+
+ if ($index >= $numpages) {
+ $zinc->lower($divgroup);
+
+ } else {
+ $zinc->raise($divgroup);
+ $zinc->itemconfigure("(div2 && $index)", -visible => 1);
+ $zinc->coords("(div2 && $index && intercalaire)", $shapes->[$index]);
+ $zinc->coords("(div2 && $index && titre)", $tcoords->[$index]);
+ $zinc->itemconfigure("(div2 && $index && titre)", -text => $tabtable->{'-titles'}->[$index]);
+ }
+
+ }
+
+ &selectDivider('div2', 0);
+}
+
+
+#-----------------------------------------------------------------------------------
+# Callback CATCH de sélection (début de déplacement) des items tagés 'move'
+#-----------------------------------------------------------------------------------
+sub mobileStart {
+ my $ev = $zinc->XEvent;
+ ($dx, $dy) = (0 - $ev->x, 0 - $ev->y);
+
+ $zinc->raise('current');
+
+}
+
+
+#-----------------------------------------------------------------------------------
+# Callback MOVE de déplacement des items tagés 'move'
+#-----------------------------------------------------------------------------------
+sub mobileMove {
+ my $ev = $zinc->XEvent;
+ $zinc->translate('current', $ev->x + $dx, $ev->y +$dy);
+ ($dx, $dy) = (0 - $ev->x, 0 - $ev->y);
+
+}
+
+
+#-----------------------------------------------------------------------------------
+# Callback RELEASE de relaché (fin de déplacement) des items tagés 'move'
+#-----------------------------------------------------------------------------------
+sub mobileStop {
+ &mobileMove;
+}
+
+
+sub viewTranslate {
+ my $way = shift;
+
+ my $dx = ($way eq 'left') ? -10 : ($way eq 'right') ? 10 : 0;
+ my $dy = ($way eq 'up') ? -10 : ($way eq 'down') ? 10 : 0;
+
+ $zinc->translate($curview, $dx, $dy);
+
+}
+
+sub viewZoom {
+ my $key = shift;
+ my $scaleratio = ($key eq 'up') ? 1+$zoomfactor : 1-$zoomfactor;
+
+ $zinc->scale($curview, $scaleratio, $scaleratio);
+
+}
+
+sub viewRotate {
+ my $way = shift;
+ my $delta_angle = $rotate_angle;
+
+ $delta_angle *= -1 if ($way eq 'cw');
+
+ $zinc->rotate($curview, $delta_angle);
+
+}
+
+
+
+
+sub buildTabBox {
+ my ($zinc, $parentgroup, $style, $name) = @_;
+ my $params = delete $style->{'-params'};
+ my @tags = @{$params->{'-tags'}};
+ my $coords = delete $style->{'-coords'};
+ my $table = $tabtable{$style->{'-anchor'}};
+ my $titles = $style->{'-tabtitles'};
+ my ($shapes, $tcoords, $invert) = &TabBoxCoords($coords, %{$style});
+ my $k = ($invert) ? scalar @{$shapes} : -1;
+ foreach my $shape (reverse @{$shapes}) {
+ $k += ($invert) ? -1 : +1;
+ my $group = $zinc->add('group', $parentgroup);
+ $params->{'-tags'} = [$tags[0], $k, $tags[1], 'intercalaire'];
+ $zinc->add('curve', $group, $shape, %{$params});
+
+ if ($style->{'-page'}) {
+ &buildZincItem($zinc, $group, $style->{'-page'});
+ }
+
+ my $tindex = ($invert) ? $k : $#{$shapes} - $k;
+ if ($titles) {
+ my $titltags = [$tags[0], $k, $tags[1], 'titre'];
+ $zinc->add('text', $group,
+ -position => $tcoords->[$tindex],
+ -text => $titles->[$tindex],
+ -font => $font_9b,
+ -alignment => 'center',
+ -anchor => 'center',
+ -color => '#000099',
+ -priority => 200,
+ -tags => $titltags,
+ );
+
+ }
+
+ # exemple fonte
+ if ($tindex == 0) {
+ $zinc->add('text', $parentgroup,
+ -position => [-130, -84],
+ -text => $table->{'-names'}->[0],
+ -font => $font_9b,
+ -alignment => 'left',
+ -anchor => 'w',
+ -color => '#000000',
+ -priority => 500,
+ -tags => [$tags[0], 'fontname'],
+ );
+
+ # as I do not want to put lot of images in TkZinc demos, I
+ # commented out the following lines!
+# $zinc->add('icon', $parentgroup,
+# -position => [-130, -70],
+# -priority => 200,
+# -image => $table->{'-images'}->[0],
+# -tags => [$tags[0], 'fontimage'],
+# );
+ }
+
+ }
+
+ &selectDivider($tags[0], $k);
+}
+
+
+1;
diff --git a/Perl/demos/zinc-demos b/Perl/demos/zinc-demos
index 414a0a6..88fb9bd 100644
--- a/Perl/demos/zinc-demos
+++ b/Perl/demos/zinc-demos
@@ -183,12 +183,13 @@ $T->insert('end', "4. Transformation testbed.\n", [qw/demo demo-transforms/]
$T->insert('end', "5. zooming/Rotating icons. (requires openGL)\n", [qw/demo demo-icon_zoom_resize/]);
$T->insert('end', "\n", '', "Use of openGL\n", 'title');
-$T->insert('end', "1. The TkZinc Logo (requires openGL).\n", [qw/demo demo-tkZincLogo/]);
+$T->insert('end', "1. A zoomable/rotatable TkZinc Logo (better with openGL).\n", [qw/demo demo-tkZincLogo/]);
$T->insert('end', "2. Axial color variation on the X axis (requires openGL).\n", [qw/demo demo-color-x/]);
$T->insert('end', "3. Axial color variation on the Y axis (requires openGL).\n", [qw/demo demo-color-y/]);
$T->insert('end', "4. Circular color variation (requires openGL).\n", [qw/demo demo-color-circular/]);
$T->insert('end', "5. Path and Conical color variations (requires openGL).\n", [qw/demo demo-color-path-and-conic/]);
$T->insert('end', "6. The triangles item (requires openGL).\n", [qw/demo demo-triangles/]);
+$T->insert('end', "7. A set of demos based on Graphics.pm module (really better with openGL).\n", [qw/demo demo-testGraphics/]);
@@ -248,7 +249,9 @@ sub AUTOLOAD {
{
$DEMO_FILE = "$WIDTRIB/${demo}.pl" if -f "$WIDTRIB/${demo}.pl";
$DEMO_FILE = "$zinc_lib/${demo}.pl" if -f "$zinc_lib/${demo}.pl";
- do $DEMO_FILE;
+ if (defined $DEMO_FILE) {
+ do $DEMO_FILE ;
+ } else { warn "No such demo: $demo.pl in either $WIDTRIB/ or $zinc_lib/\n"; }
warn $@ if $@;
}
$T->Unbusy;