The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

# $Id: transforms.pl,v 1.7 2003/09/24 15:10:39 mertz Exp $
# This simple demo has been developped by P. Lecoanet <lecoanet@cena.fr>

use vars qw( $VERSION );
($VERSION) = sprintf("%d.%02d", q$Revision: 1.7 $ =~ /(\d+)\.(\d+)/);

#
# TODO:
#
# Ajouter la construction des items manquants
#

use Tk;
use Tk::Zinc;

my $currentgroup;
my $currentitem;
my $mw = MainWindow->new();
my $top = 1;

my $inactiveAxisColor = 'black';
my $activeAxisColor = 'red';
my $worldAxisColor = '#a5a5a5';

my $itemtype;
my $composerot = 1;
my $composescale = 1;
my $drag = 0;

my $logo = $mw->Photo(-file => Tk->findINC('demos/zinc_data/zinc_anti.gif'));

my $text = $mw->Text(-relief => 'sunken',
		     -borderwidth => 2,
		     -height => 12);
$text->pack(-expand => 0, -fill => 'x');
$text->insert('0.0', 'Items are always added to the current group.
The available commands are:
   Button 1       on the background, add an item with initial translation
   Button 2       on the background, add a group with initial translation
   Button 1       on item/group axes, select/deselect that item space
   Drag Button 1  on item/group axes, translate that item space
   Del            reset the transformation
   Shift-Del      reset a group direct children transformations
   PageUp/Down    scale up/down
   End/Home       rotate right/left
   Ins            swap the Y axis
   4 arrows       translate in the 4 directions');
$text->configure(-state => 'disabled');

my $zinc = $mw->Zinc(-borderwidth => 3,
		     -highlightthickness => 0,
		     -relief => 'sunken',
		     -render => 0,
		     -takefocus => 1);
$zinc->pack(-expand => 1, -fill => 'both');
$zinc->configure(-width => 500, -height => 500);

my $rc = $mw->Frame()->pack(-expand => 0, -fill => 'x');
my $option = $rc->Optionmenu(-options => ['rectangle', 'arc',
					  'curve',
					  'icon', 'tabular',
					  'text', 'track',
					  'triangles', 'waypoint'],
#			     -command => sub { $zinc->Tk::focus(); },
			     -variable => \$itemtype)->grid(-row => 0,
							    -column => 1,
							    -sticky => 'w');

$rc->Button(-text => 'Add item',
	    -command => \&additem)->grid(-row => 0,
					 -column => 2,
					 -padx => 10,
					 -sticky => 'ew');
$rc->Button(-text => 'Add group',
	    -command => \&addgroup)->grid(-row => 0,
					  -column => 3,
					  -padx => 10,
					  -sticky => 'ew');
$rc->Button(-text => 'Remove',
	    -command => \&removeitem)->grid(-row => 0,
					    -column => 4,
					    -padx => 10,
					    -sticky => 'ew');

$rc->Checkbutton(-text => '-composescale',
		 -command => \&togglecomposescale,
		 -variable => \$composescale)->grid(-row => 0,
						    -column => 6,
						    -sticky => 'w');

$rc->Checkbutton(-text => '-composesrotation',
		 -command => \&togglecomposerot,
		 -variable => \$composerot)->grid(-row => 1,
						  -column => 6,
						  -sticky => 'w');


my $world = $zinc->add('group', $top);
$zinc->add('curve', $top, [0, 0, 80, 0],
	   -linewidth => 3,
	   -linecolor => $worldAxisColor,
	   -lastend => [6,8,3],
	   -tags => ["axis:$world"]);
$zinc->add('curve', $top, [0, 0, 0, 80],
	   -linewidth => 3,
	   -linecolor => $worldAxisColor,
	   -lastend => [6,8,3],
	   -tags => ["axis:$world"]);
$zinc->add('rectangle', $top, [-2, -2, 2, 2],
	   -filled => 1,
	   -fillcolor => $worldAxisColor,
	   -linecolor => $worldAxisColor,
	   -linewidth => 3,
	   -tags => ["axis:$world"]);
$zinc->add('text', $top,
	   -text => "This is the origin\nof the world",
	   -anchor => 's',
	   -color => $worldAxisColor,
	   -alignment => 'center',
	   -tags => ["axis:$world", 'text']);


$currentgroup = $world;

$zinc->Tk::bind('<1>', [\&mouseadd, 'item']);
$zinc->Tk::bind('<2>', [\&mouseadd, 'group']);
$zinc->Tk::bind('<Up>', \&moveup);
$zinc->Tk::bind('<Left>', \&moveleft);
$zinc->Tk::bind('<Right>', \&moveright);
$zinc->Tk::bind('<Down>', \&movedown);
$zinc->Tk::bind('<Next>', \&scaledown);
$zinc->Tk::bind('<Prior>', \&scaleup);
$zinc->Tk::bind('<Delete>', \&reset);
$zinc->Tk::bind('<Shift-Delete>', \&resetchildren);
$zinc->Tk::bind('<End>', \&rotateleft);
$zinc->Tk::bind('<Home>', \&rotateright);
$zinc->Tk::bind('<Insert>', \&swapaxis);

$zinc->Tk::bind('<Configure>', [\&resize]);

$zinc->Tk::focus();
$zinc->focusFollowsMouse();


MainLoop();

sub resize {
  my $ev = $zinc->XEvent();
  my $x = $ev->w/2;
  my $y = $ev->h/2;

  $zinc->treset($world);
  $zinc->treset("axis:$world");
  $zinc->translate($world, $x, $y);
  $zinc->translate("axis:$world", $x, $y);
}

sub swapaxis {
  if (defined($currentitem)) {
    $zinc->scale($currentitem, 1, -1);
    $zinc->scale("axisgrp:$currentitem", 1, -1);
  }
}

sub togglecomposerot {
  if (defined($currentitem)) {
    $zinc->itemconfigure($currentitem, -composerotation => $composerot);
    $zinc->itemconfigure("axisgrp:$currentitem", -composerotation => $composerot);
  }
}

sub togglecomposescale {
  my $bool;

  if (defined($currentitem)) {
    $zinc->itemconfigure($currentitem, -composescale => $composescale);
    $zinc->itemconfigure("axisgrp:$currentitem", -composescale => $composescale);
  }
}

sub dragitem {
  $drag = 1;
  return if (!defined($currentitem));

  my $ev = $zinc->XEvent();
  my $group = $zinc->group($currentitem);
  my ($x, $y) = $zinc->transform($group, [$ev->x, $ev->y]);

  $zinc->treset($currentitem);
  $zinc->treset("axisgrp:$currentitem");
  $zinc->translate($currentitem, $x, $y);
  $zinc->translate("axisgrp:$currentitem", $x, $y);
}

sub select {
  my @tags = $zinc->gettags('current');
  my $t;
  foreach $t (@tags) {
    if ($t =~ '^axis:(\d+)') {
      changeitem($1);
    }
  }
}

sub changeitem {
  my ($item) = @_;

  if (defined($currentitem) && !$drag) {
    $zinc->itemconfigure("axis:$currentitem && !text",
			 -linecolor => $inactiveAxisColor,
			 -fillcolor => $inactiveAxisColor);
    if ($currentitem != $currentgroup) {
      $zinc->itemconfigure("axis:$currentitem && !text",
			   -linewidth => 1);
    }
  }
  if (!defined($currentitem) || ($item != $currentitem)) {
    $zinc->itemconfigure("axis:$item && !text",
			 -linecolor => $activeAxisColor,
			 -fillcolor => $activeAxisColor,
			 -linewidth => 3);
    $currentitem = $item;
    $composerot = $zinc->itemcget($currentitem, -composerotation);
    $zinc->itemconfigure("axisgrp:$currentitem", -composerotation => $composerot);
    $composescale = $zinc->itemcget($currentitem, -composescale);
    $zinc->itemconfigure("axisgrp:$currentitem", -composescale => $composescale);
  }
  elsif (!$drag) {
    $currentitem = undef;
    $composerot = $composescale = 1;
  }
  $drag = 0;
}

sub selectgroup {
  my @tags = $zinc->gettags('current');
  my $t;
  foreach $t (@tags) {
    if ($t =~ '^axis:(\d+)') {
      changegroup($1);
    }
  }
}

sub changegroup {
  my ($grp) = @_;

  changeitem($grp);
  $zinc->itemconfigure("axis:$currentgroup && !text",
		       -linewidth => 1);
  if (defined($currentitem)) {
    $currentgroup = $currentitem;
  }
  else {
    $currentgroup = $world;
  }

  $zinc->itemconfigure("axis:$currentgroup && !text",
		       -linewidth => 3);
}

sub reset {
  if (defined($currentitem)) {
    $zinc->treset($currentitem);
    $zinc->treset("axisgrp:$currentitem");
  }
}

sub resetchildren {
  my @children;

  if (defined($currentitem) && ($zinc->type($currentitem) eq 'group')) {
    @children = $zinc->addtag('rt', 'withtag', 'all', $currentitem, 0);
    $zinc->treset('rt');
    $zinc->dtag('rt', 'rt');
  }
}

sub moveup {
  move(0, 20);
}

sub movedown {
  move(0, -20);
}

sub moveright {
  move(20, 0);
}

sub moveleft {
  move(-20, 0);
}

sub move {
  my ($dx, $dy) = @_;

  if (defined($currentitem)) {
    $zinc->translate($currentitem, $dx, $dy);
    $zinc->translate("axisgrp:$currentitem", $dx, $dy);
  }
}

sub scaleup {
  scale(1.1, 1.1);
}

sub scaledown {
  scale(0.9, 0.9);
}

sub scale {
  my ($dx, $dy) = @_;

  if (defined($currentitem)) {
    $zinc->scale($currentitem, $dx, $dy);
    $zinc->scale("axisgrp:$currentitem", $dx, $dy);
  }
}

sub rotateleft {
  rotate(-3.14159/18);
}

sub rotateright {
  rotate(3.14159/18);
}

sub rotate {
  my ($angle) = @_;

  if (defined($currentitem)) {
    $zinc->rotate($currentitem, $angle);
    $zinc->rotate("axisgrp:$currentitem", $angle);
  }
}

sub newrect {
  return $zinc->add('rectangle', $currentgroup,
		    [-15, -15, 15, 15],
		    -filled => 1,
		    -linewidth => 0,
		    -fillcolor => 'tan');
}

sub newarc {
  return $zinc->add('arc', $currentgroup,
		    [-25, -15, 25, 15],
		    -filled => 1,
		    -linewidth => 0,
		    -fillcolor => 'tan');
}

sub newcurve {
  return $zinc->add('curve', $currentgroup,
		    [-15, -15, -15, 15, 15, 15, 15, -15],
		    -filled => 1,
		    -linewidth => 0,
		    -fillcolor => 'tan');
}

sub newtext {
  my $item = $zinc->add('text', $currentgroup,
			-anchor => 's');
  $zinc->itemconfigure($item, -text => "Item id: $item");
  return $item;
}

sub newicon {
  my $item = $zinc->add('icon', $currentgroup,
			-image => $logo,
			-anchor => 'center');

  return $item;
}

sub newtriangles {
  my $item = $zinc->add('triangles', $currentgroup,
			[-25, 15, -10, -15, 5, 15,
			 20, -15, 35, 15, 50, -30],
			-colors => ['tan', 'wheat', 'tan', 'wheat']);
  return $item;
}

sub newtrack {
  my $labelformat = "x80x50+0+0 a0a0^0^0 a0a0^0>1 a0a0>2>1 x30a0>3>1 a0a0^0>2";

  my $item=$zinc->add('track', $currentgroup, 6,
		      -labelformat => $labelformat,
		      -speedvector => [30, -15],
		      -markersize => 20);
  $zinc->itemconfigure($item, 0,
		       -filled => 0,
		       -bordercolor => 'DarkGreen',
		       -border => 'contour');
  $zinc->itemconfigure($item, 1,
		       -filled => 1,
		       -backcolor => 'gray60',
		       -text => 'AFR6128');
  $zinc->itemconfigure($item, 2,
		       -filled => 0,
		       -backcolor => 'gray65',
		       -text => '390');
  $zinc->itemconfigure($item, 3,
		       -filled => 0,
		       -backcolor => 'gray65',
		       -text => '/');
  $zinc->itemconfigure($item, 4,
		       -filled => 0,
		       -backcolor => 'gray65',
		       -text => '350');
  $zinc->itemconfigure($item, 5,
		       -filled => 0,
		       -backcolor => 'gray65',
		       -text => 'TUR');
  return $item;
}

sub newwaypoint {
  my $labelformat = "a0a0+0+0 a0a0>0^1";

  my $item=$zinc->add('waypoint', $currentgroup, 2,
		      -labelformat => $labelformat);
  $zinc->itemconfigure($item, 0,
		       -filled => 1,
		       -backcolor => 'DarkGreen',
		       -text => 'TUR');
  $zinc->itemconfigure($item, 1,
		       -text => '>>>');
  return $item;
}

sub newtabular {
  my $labelformat = "f700f600+0+0 f700a0^0^0 f700a0^0>1 f700a0^0>2 f700a0^0>3 f700a0^0>4 f700a0^0>5";

  my $item=$zinc->add('tabular', $currentgroup, 7,
		      -labelformat => $labelformat);
  $zinc->itemconfigure($item, 0,
		       -filled => 1,
		       -border => 'contour',
		       -bordercolor => 'black',
		       -backcolor => 'gray60');
  $zinc->itemconfigure($item, 1,
		       -alignment => 'center',
		       -text => 'AFR6128');
  $zinc->itemconfigure($item, 2,
		       -alignment => 'center',
		       -text => '390');
  $zinc->itemconfigure($item, 3,
		       -alignment => 'center',
		       -text => '370');
  $zinc->itemconfigure($item, 4,
		       -alignment => 'center',
		       -text => '350');
  $zinc->itemconfigure($item, 5,
		       -alignment => 'center',
		       -text => '330');
  $zinc->itemconfigure($item, 6,
		       -alignment => 'center',
		       -text => 'TUR');
  return $item;
}

sub addaxes {
  my ($item, $length, $command, $infront) = @_;

  my $axesgroup = $zinc->add('group', $currentgroup,
			     -tags => ["axisgrp:$item"]);
  $zinc->add('curve', $axesgroup, [0, 0, $length, 0],
	     -linewidth => 2,
	     -lastend => [6,8,3],
	     -tags => ["axis:$item"]);
  $zinc->add('curve', $axesgroup, [0, 0, 0, $length],
	     -linewidth => 2,
	     -lastend => [6,8,3],
	     -tags => ["axis:$item"]);
  $zinc->add('rectangle', $axesgroup, [-2, -2, 2, 2],
	     -filled => 1,
	     -linewidth => 0,
	     -composescale => 0,
	     -tags => ["axis:$item"]);
  if ($infront) {
    $zinc->raise($item, $axesgroup);
  }
  $zinc->bind("axis:$item", '<B1-Motion>', \&dragitem);
  $zinc->bind("axis:$item", '<ButtonRelease-1>', $command);
}

sub additem {
  my $item;
  my $length = 25;
  my $itemontop = 0;

  if ($itemtype eq 'rectangle') {
    $item = newrect();
  }
  elsif ($itemtype eq 'arc') {
    $item = newarc();
  }
  elsif ($itemtype eq 'curve') {
    $item = newcurve();
  }
  elsif ($itemtype eq 'triangles') {
    $item = newtriangles();
  }
  elsif ($itemtype eq 'icon') {
    $item = newicon();
  }
  elsif ($itemtype eq 'text') {
    $item = newtext();
  }
  elsif ($itemtype eq 'track') {
    $item = newtrack();
    $itemontop = 1;
  }
  elsif ($itemtype eq 'waypoint') {
    $item = newwaypoint();
    $itemontop = 1;
  }
  elsif ($itemtype eq 'tabular') {
    $item = newtabular();
  }

  addaxes($item, 25, \&select, $itemontop);
  changeitem($item);
}

sub addgroup {
  my $item = $zinc->add('group', $currentgroup);

  addaxes($item, 80, \&selectgroup, 1);
  changegroup($item);
}

sub mouseadd {
  my ($w, $itemorgrp) = @_;
  my $ev = $zinc->XEvent();
  my ($x, $y) = $zinc->transform($currentgroup, [$ev->x, $ev->y]);
  my $item = $zinc->find('withtag', 'current');

  if (defined($item)) {
    my @tags = $zinc->gettags($item);
    foreach my $t (@tags) {
      return if ($t =~ '^axis');
    }
  }
  if ($itemorgrp eq 'group') {
    addgroup();
  }
  else {
    additem();
  }
  $zinc->translate($currentitem, $x, $y);
  $zinc->translate("axisgrp:$currentitem", $x, $y);
}

sub removeitem {
  if (defined($currentitem)) {
    $zinc->remove($currentitem, "axisgrp:$currentitem");
    if ($currentitem == $currentgroup) {
      $currentgroup = $world;
    }
    $currentitem = undef;
    $composescale = $composerot = 1;
  }
}