line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# 524JDkqX: Tk::AbstractCanvas.pm by PipStuart , derived from Tk::WorldCanvas (by JosephSkrovan |
2
|
|
|
|
|
|
|
# which was based on a version by RudyAlbachten ) and Tk::RotCanvas (by AlaQumsieh ). |
3
|
|
|
|
|
|
|
package Tk::AbstractCanvas; # This module provides an alternative to Tk::Canvas which is able to zoom, pan, and rotate. |
4
|
|
|
|
|
|
|
require Tk::Derived; |
5
|
|
|
|
|
|
|
require Tk::Canvas; |
6
|
1
|
|
|
1
|
|
8673
|
use strict;use warnings;use utf8; |
|
1
|
|
|
1
|
|
3
|
|
|
1
|
|
|
1
|
|
22
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
22
|
|
|
1
|
|
|
|
|
467
|
|
|
1
|
|
|
|
|
12
|
|
|
1
|
|
|
|
|
6
|
|
7
|
1
|
|
|
1
|
|
342
|
use Tk; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
use Carp; |
9
|
|
|
|
|
|
|
our $VERSION = '1.8';my $d8VS='H79M9Rot'; |
10
|
|
|
|
|
|
|
@Tk::AbstractCanvas::ISA = qw(Tk::Derived Tk::Canvas); Construct Tk::Widget 'AbstractCanvas'; |
11
|
|
|
|
|
|
|
sub Tk::Widget::ScrlACnv { shift->Scrolled('AbstractCanvas'=>@_) } |
12
|
|
|
|
|
|
|
my %_can_rotate_about_center = ( # If objects can't rotate about their center, their center can at least rotate about another point on the canvas. |
13
|
|
|
|
|
|
|
line => 1, |
14
|
|
|
|
|
|
|
polygon => 1, |
15
|
|
|
|
|
|
|
); |
16
|
|
|
|
|
|
|
my %_rotate_methods = ( |
17
|
|
|
|
|
|
|
line => \&_rotate_line, |
18
|
|
|
|
|
|
|
text => \&_rotate_line, |
19
|
|
|
|
|
|
|
image => \&_rotate_line, |
20
|
|
|
|
|
|
|
bitmap => \&_rotate_line, |
21
|
|
|
|
|
|
|
window => \&_rotate_line, |
22
|
|
|
|
|
|
|
rectangle => \&_rotate_rect, |
23
|
|
|
|
|
|
|
arc => \&_rotate_rect, |
24
|
|
|
|
|
|
|
grid => \&_rotate_rect, |
25
|
|
|
|
|
|
|
oval => \&_rotate_rect, |
26
|
|
|
|
|
|
|
polygon => \&_rotate_poly, |
27
|
|
|
|
|
|
|
); |
28
|
|
|
|
|
|
|
use constant PI => 3.14159269; |
29
|
|
|
|
|
|
|
sub ClassInit { my($acnv, $mwin)= @_; $acnv->SUPER::ClassInit($mwin); } |
30
|
|
|
|
|
|
|
sub InitObject { |
31
|
|
|
|
|
|
|
my($acnv, $args)= @_; |
32
|
|
|
|
|
|
|
my $pdat = $acnv->privateData(); |
33
|
|
|
|
|
|
|
$pdat->{'bbox' } = [0, 0, -1, -1]; |
34
|
|
|
|
|
|
|
$pdat->{'scale' } = 1 ; |
35
|
|
|
|
|
|
|
$pdat->{'movex' } = 0 ; |
36
|
|
|
|
|
|
|
$pdat->{'movey' } = 0 ; |
37
|
|
|
|
|
|
|
$pdat->{'bboxvalid' } = 1 ; |
38
|
|
|
|
|
|
|
$pdat->{'inverty' } = 0 ; # I guess these options are not really private since they have accessors but I'm not sure where better to store them |
39
|
|
|
|
|
|
|
$pdat->{'rect_to_poly' } = 0 ; |
40
|
|
|
|
|
|
|
$pdat->{'oval_to_poly' } = 0 ; |
41
|
|
|
|
|
|
|
$pdat->{'control_nav' } = 0 ; |
42
|
|
|
|
|
|
|
$pdat->{'control_nav_busy' } = 0 ; # flag to know not to allow other navs |
43
|
|
|
|
|
|
|
$pdat->{'control_zoom_scale'} = -0.001; |
44
|
|
|
|
|
|
|
$pdat->{'control_rot_scale' } = -0.3 ; |
45
|
|
|
|
|
|
|
$pdat->{'control_rot_mocb' } = undef; # MOtion CallBack |
46
|
|
|
|
|
|
|
$pdat->{'control_rot_rlcb' } = undef; # ReLease CallBack |
47
|
|
|
|
|
|
|
$pdat->{'eventx' } = -1 ; |
48
|
|
|
|
|
|
|
$pdat->{'eventy' } = -1 ; |
49
|
|
|
|
|
|
|
$pdat->{'width' } = $acnv->width( ); |
50
|
|
|
|
|
|
|
$pdat->{'height' } = $acnv->height(); |
51
|
|
|
|
|
|
|
$acnv->configure(-confine => 0); |
52
|
|
|
|
|
|
|
$acnv->ConfigSpecs( '-bandColor' => ['PASSIVE', 'bandColor', 'BandColor', 'red' ], '-bandcolor' => '-bandColor', |
53
|
|
|
|
|
|
|
'-changeView' => ['CALLBACK', 'changeView', 'ChangeView', undef], '-changeview' => '-changeView'); |
54
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
55
|
|
|
|
|
|
|
my $widt = $acnv->width( ); my $pwid = $pdat->{'width' }; |
56
|
|
|
|
|
|
|
my $hite = $acnv->height(); my $phit = $pdat->{'height'}; |
57
|
|
|
|
|
|
|
if($widt != $pwid || $hite != $phit) { |
58
|
|
|
|
|
|
|
my $bwid = $acnv->cget('-borderwidth'); _view_area_canvas($acnv, $bwid, $bwid, $pwid - $bwid, $phit - $bwid); |
59
|
|
|
|
|
|
|
$pdat->{'width' } = $widt; $pdat->{'height'} = $hite; my $bbox = $pdat->{'bbox'}; |
60
|
|
|
|
|
|
|
my $left = $acnv->canvasx($bwid); my $rite = $acnv->canvasx($widt - $bwid); |
61
|
|
|
|
|
|
|
my $topp = $acnv->canvasy($bwid); my $botm = $acnv->canvasy($hite - $bwid); |
62
|
|
|
|
|
|
|
$acnv->viewAll() if(_inside(@$bbox, $left, $topp, $rite, $botm)); |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
}); |
65
|
|
|
|
|
|
|
$acnv->SUPER::InitObject($args); |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
sub invertY { my $acnv = shift(); my $pdat = $acnv->privateData(); $pdat->{'inverty' } = shift() if(@_); return($pdat->{'inverty' }); } |
68
|
|
|
|
|
|
|
sub rectToPoly { my $acnv = shift(); my $pdat = $acnv->privateData(); $pdat->{'rect_to_poly'} = shift() if(@_); return($pdat->{'rect_to_poly'}); } |
69
|
|
|
|
|
|
|
sub ovalToPoly { my $acnv = shift(); my $pdat = $acnv->privateData(); $pdat->{'oval_to_poly'} = shift() if(@_); return($pdat->{'oval_to_poly'}); } |
70
|
|
|
|
|
|
|
sub controlNav { |
71
|
|
|
|
|
|
|
my $acnv = shift(); my $pdat = $acnv->privateData(); |
72
|
|
|
|
|
|
|
if(@_) { |
73
|
|
|
|
|
|
|
$pdat->{'control_nav'} = shift(); |
74
|
|
|
|
|
|
|
if($pdat->{'control_nav'}) { |
75
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
76
|
|
|
|
|
|
|
if(!$pdat->{'control_nav_busy'} && $pdat->{'eventx'} == -1 && $pdat->{'eventy'} == -1) { |
77
|
|
|
|
|
|
|
$pdat->{'control_nav_busy'} = 1; |
78
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'})= $acnv->eventLocation(); |
79
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
80
|
|
|
|
|
|
|
my($evtx, $evty)= $acnv->eventLocation(); my($left, $botm, $rite, $topp)= $acnv->getView(); |
81
|
|
|
|
|
|
|
my($cntx, $cnty)=(($left + $rite) / 2, ($botm + $topp) / 2); |
82
|
|
|
|
|
|
|
for($acnv->find('all')) { |
83
|
|
|
|
|
|
|
$acnv->rotate($_, (($pdat->{'eventx'} - $evtx) + ($pdat->{'eventy'} - $evty)) * $pdat->{'control_rot_scale'} * $pdat->{'scale'}, $cntx, $cnty); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'})=($evtx, $evty); |
86
|
|
|
|
|
|
|
$pdat->{'control_rot_mocb'}->() if($pdat->{'control_rot_mocb'}); |
87
|
|
|
|
|
|
|
}); |
88
|
|
|
|
|
|
|
my $rcrf = sub { # Release Code ReF for either Control-Key or MouseButton Release binding restoration |
89
|
|
|
|
|
|
|
my($evtx, $evty)= $acnv->eventLocation(); my($left, $botm, $rite, $topp)= $acnv->getView(); |
90
|
|
|
|
|
|
|
my($cntx, $cnty)=(($left + $rite) / 2, ($botm + $topp) / 2); |
91
|
|
|
|
|
|
|
for($acnv->find('all')) { |
92
|
|
|
|
|
|
|
$acnv->rotate($_, (($pdat->{'eventx'} - $evtx) + ($pdat->{'eventy'} - $evty)) * $pdat->{'control_rot_scale'} * $pdat->{'scale'}, $cntx, $cnty); |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'})=(-1, -1); |
95
|
|
|
|
|
|
|
$pdat->{'control_rot_rlcb'}->() if($pdat->{'control_rot_rlcb'}); |
96
|
|
|
|
|
|
|
$pdat->{'control_nav_busy'} = 0; # maybe control_nav_busy should be cleared before calling the ReLease CallBack? |
97
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
98
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
99
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => ''); |
100
|
|
|
|
|
|
|
}; |
101
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => $rcrf); |
102
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => $rcrf); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
}); |
105
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
106
|
|
|
|
|
|
|
if(!$pdat->{'control_nav_busy'} && $pdat->{'eventx'} == -1 && $pdat->{'eventy'} == -1) { |
107
|
|
|
|
|
|
|
$pdat->{'control_nav_busy'} = 1; |
108
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'})= $acnv->eventLocation(); |
109
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
110
|
|
|
|
|
|
|
my($evtx, $evty)= $acnv->eventLocation(); |
111
|
|
|
|
|
|
|
$acnv->panAbstract( $pdat->{'eventx'} - $evtx, $pdat->{'eventy'} - $evty); |
112
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'}) = $acnv->eventLocation(); |
113
|
|
|
|
|
|
|
}); |
114
|
|
|
|
|
|
|
my $rcrf = sub { # Release Code ReF for either Control-Key or MouseButton Release binding restoration |
115
|
|
|
|
|
|
|
my($evtx, $evty)= $acnv->eventLocation(); |
116
|
|
|
|
|
|
|
$acnv->panAbstract( $pdat->{'eventx'} - $evtx, $pdat->{'eventy'} - $evty); |
117
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'})=(-1, -1); |
118
|
|
|
|
|
|
|
$pdat->{'control_nav_busy'} = 0; |
119
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
120
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
121
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => ''); |
122
|
|
|
|
|
|
|
}; |
123
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => $rcrf); |
124
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => $rcrf); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
}); |
127
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
128
|
|
|
|
|
|
|
if(!$pdat->{'control_nav_busy'} && $pdat->{'eventx'} == -1 && $pdat->{'eventy'} == -1) { |
129
|
|
|
|
|
|
|
$pdat->{'control_nav_busy'} = 1; |
130
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'})= $acnv->eventLocation(); |
131
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
132
|
|
|
|
|
|
|
my($evtx, $evty)= $acnv->eventLocation(); |
133
|
|
|
|
|
|
|
$acnv->zoom(1.0 + (($pdat->{'eventx'} - $evtx) + ($pdat->{'eventy'} - $evty)) * $pdat->{'control_zoom_scale'} * $pdat->{'scale'}); |
134
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'})=($evtx, $evty); |
135
|
|
|
|
|
|
|
}); |
136
|
|
|
|
|
|
|
my $rcrf = sub { # Release Code ReF for either Control-Key or MouseButton Release binding restoration |
137
|
|
|
|
|
|
|
my($evtx, $evty)= $acnv->eventLocation(); |
138
|
|
|
|
|
|
|
$acnv->zoom(1.0 + (($pdat->{'eventx'} - $evtx) + ($pdat->{'eventy'} - $evty)) * $pdat->{'control_zoom_scale'} * $pdat->{'scale'}); |
139
|
|
|
|
|
|
|
($pdat->{'eventx'}, $pdat->{'eventy'})=(-1, -1); |
140
|
|
|
|
|
|
|
$pdat->{'control_nav_busy'} = 0; |
141
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
142
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
143
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => ''); |
144
|
|
|
|
|
|
|
}; |
145
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => $rcrf); |
146
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => $rcrf); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
}); |
149
|
|
|
|
|
|
|
} else { |
150
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
151
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
152
|
|
|
|
|
|
|
$acnv->CanvasBind('' => ''); |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
return($pdat->{'control_nav'}); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
sub controlNavBusy { my $acnv = shift; my $pdat = $acnv->privateData(); $pdat->{'control_nav_busy' } = shift if(@_); return($pdat->{'control_nav_busy' });} |
158
|
|
|
|
|
|
|
sub controlZoomScale { my $acnv = shift; my $pdat = $acnv->privateData(); $pdat->{'control_zoom_scale'} = shift if(@_); return($pdat->{'control_zoom_scale'});} |
159
|
|
|
|
|
|
|
sub controlRotScale { my $acnv = shift; my $pdat = $acnv->privateData(); $pdat->{'control_rot_scale' } = shift if(@_); return($pdat->{'control_rot_scale' });} |
160
|
|
|
|
|
|
|
sub controlRotMoCB { my $acnv = shift; my $pdat = $acnv->privateData(); $pdat->{'control_rot_mocb' } = shift if(@_); return($pdat->{'control_rot_mocb' });} |
161
|
|
|
|
|
|
|
sub controlRotRlCB { my $acnv = shift; my $pdat = $acnv->privateData(); $pdat->{'control_rot_rlcb' } = shift if(@_); return($pdat->{'control_rot_rlcb' });} |
162
|
|
|
|
|
|
|
sub controlScale { my $acnv = shift; my $pdat = $acnv->privateData(); $pdat->{'scale' } = shift if(@_); return($pdat->{'scale' });} |
163
|
|
|
|
|
|
|
sub eventX { my $acnv = shift; my $pdat = $acnv->privateData(); $pdat->{'eventx' } = shift if(@_); return($pdat->{'eventx' });} |
164
|
|
|
|
|
|
|
sub eventY { my $acnv = shift; my $pdat = $acnv->privateData(); $pdat->{'eventy' } = shift if(@_); return($pdat->{'eventy' });} |
165
|
|
|
|
|
|
|
sub rotate { # takes as input id of obj to rot, angle to rot by, && optional x,y point to rot about instead of obj center, if possible |
166
|
|
|
|
|
|
|
my($self, $obid, $angl, $xfoc, $yfoc)= @_; croak "rotate: Must supply an angle -" unless(defined($angl)); |
167
|
|
|
|
|
|
|
my $type = $self->type($obid); # some objs need a pivot point to rotate their center around |
168
|
|
|
|
|
|
|
return() unless(exists($_can_rotate_about_center{$type}) || (defined($xfoc) && defined($yfoc))); |
169
|
|
|
|
|
|
|
$_rotate_methods{$type}->($self, $obid, $angl, $xfoc, $yfoc); |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
sub _rotate_line { |
172
|
|
|
|
|
|
|
my($self, $obid, $angl, $xmid, $ymid)= @_; |
173
|
|
|
|
|
|
|
my @crds = $self->coords($obid); # get old coords && translate to origin, rot, then translate back |
174
|
|
|
|
|
|
|
unless(defined($xmid)) { |
175
|
|
|
|
|
|
|
$xmid = $crds[0] + 0.5 * ($crds[2] - $crds[0]); |
176
|
|
|
|
|
|
|
$ymid = $crds[1] + 0.5 * ($crds[3] - $crds[1]); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
my @newc; my $radi = PI * $angl / 180.0; my $sine = sin($radi); my $cosi = cos($radi); |
179
|
|
|
|
|
|
|
while(my($xcrd, $ycrd)= splice(@crds, 0, 2)) { # calc new coords |
180
|
|
|
|
|
|
|
my $xnew = $xcrd - $xmid; my $ynew = $ycrd - $ymid; |
181
|
|
|
|
|
|
|
push(@newc, $xmid + ($xnew * $cosi - $ynew * $sine)); push(@newc, $ymid + ($xnew * $sine + $ynew * $cosi)); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
$self->coords($obid, @newc); # updt coords redraws |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
sub _rotate_poly { |
186
|
|
|
|
|
|
|
my($self, $obid, $angl, $xmid, $ymid)= @_; |
187
|
|
|
|
|
|
|
my @crds = $self->coords($obid); # get old coords && poly center (of Mass) if no external rot pt given |
188
|
|
|
|
|
|
|
($xmid, $ymid)= _get_CM(@crds) unless(defined($xmid)); |
189
|
|
|
|
|
|
|
my @newc; my $radi = PI * $angl / 180.0; my $sine = sin($radi); my $cosi = cos($radi); |
190
|
|
|
|
|
|
|
while(my($xcrd, $ycrd)= splice(@crds, 0, 2)) { # Calculate the new coordinates of the poly. |
191
|
|
|
|
|
|
|
my $xnew = $xcrd - $xmid; my $ynew = $ycrd - $ymid; |
192
|
|
|
|
|
|
|
push(@newc, $xmid + ($xnew * $cosi - $ynew * $sine)); push(@newc, $ymid + ($xnew * $sine + $ynew * $cosi)); |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
$self->coords($obid, @newc); # updt coords redraws |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
sub _rotate_rect { # special rectangle rotation just for center |
197
|
|
|
|
|
|
|
my($self, $obid, $angl, $xmid, $ymid)= @_; |
198
|
|
|
|
|
|
|
my @crds = $self->coords($obid); # get old coords |
199
|
|
|
|
|
|
|
my($xomd, $yomd)=(($crds[0] + $crds[2]) / 2, ($crds[1] + $crds[3]) / 2); # original midpoint |
200
|
|
|
|
|
|
|
($xmid, $ymid)= ($xomd, $yomd) unless(defined($xmid)); |
201
|
|
|
|
|
|
|
my @newc; my $radi = PI * $angl / 180.0; my $sine = sin($radi); my $cosi = cos($radi); |
202
|
|
|
|
|
|
|
my $xnew = $xomd - $xmid; my $ynew = $yomd - $ymid; # calc coords of new midpoint |
203
|
|
|
|
|
|
|
my $xrmd = $xmid + ($xnew * $cosi - $ynew * $sine); my $yrmd = $ymid + ($xnew * $sine + $ynew * $cosi); |
204
|
|
|
|
|
|
|
while(my($xcrd, $ycrd)= splice(@crds, 0, 2)) { push(@newc, ($xcrd - $xomd) + $xrmd, ($ycrd - $yomd) + $yrmd); } |
205
|
|
|
|
|
|
|
$self->coords($obid, @newc); # updt coords redraws |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
sub getView { |
208
|
|
|
|
|
|
|
my($cnvs)= @_; my $bwid = $cnvs->cget('-borderwidth'); my $left = $bwid; my $rite = $cnvs->width() - $bwid; |
209
|
|
|
|
|
|
|
my $topp = $bwid; my $botm = $cnvs->height() - $bwid; |
210
|
|
|
|
|
|
|
return(abstractxy($cnvs, $left, $topp), abstractxy($cnvs, $rite, $botm)); |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
sub xview { |
213
|
|
|
|
|
|
|
my $cnvs = shift(); _new_bbox($cnvs) unless($cnvs->privateData->{'bboxvalid'}); $cnvs->SUPER::xview(@_); |
214
|
|
|
|
|
|
|
$cnvs->Callback('-changeView' => getView($cnvs)) if(defined($cnvs->cget('-changeView'))); |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
sub yview { |
217
|
|
|
|
|
|
|
my $cnvs = shift(); _new_bbox($cnvs) unless($cnvs->privateData->{'bboxvalid'}); $cnvs->SUPER::yview(@_); |
218
|
|
|
|
|
|
|
$cnvs->Callback('-changeView' => getView($cnvs)) if(defined($cnvs->cget('-changeView'))); |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
sub delete { |
221
|
|
|
|
|
|
|
my($cnvs, @tags)= @_; my $recr = _killBand($cnvs); my $foun = 0; # RECReate && FOUNd |
222
|
|
|
|
|
|
|
for(@tags) { if($cnvs->type($_)) { $foun = 1; last(); } } |
223
|
|
|
|
|
|
|
unless($foun) { _makeBand($cnvs) if($recr); return(); } |
224
|
|
|
|
|
|
|
my $pdat = $cnvs->privateData(); |
225
|
|
|
|
|
|
|
my($pbx1, $pby1, $pbx2, $pby2)= @{$pdat->{'bbox'}}; |
226
|
|
|
|
|
|
|
my($cbx1, $cby1, $cbx2, $cby2)= _superBbox($cnvs, @tags); |
227
|
|
|
|
|
|
|
$cnvs->SUPER::delete(@tags); |
228
|
|
|
|
|
|
|
if(!$cnvs->type('all')) { # deleted last object |
229
|
|
|
|
|
|
|
$pdat->{'bbox' } = [0, 0, -1, -1]; $pdat->{'movex'} = 0; |
230
|
|
|
|
|
|
|
$pdat->{'scale' } = 1 ; $pdat->{'movey'} = 0; |
231
|
|
|
|
|
|
|
} elsif(!_inside($cbx1, $cby1, $cbx2, $cby2, $pbx1, $pby1, $pbx2, $pby2)) { |
232
|
|
|
|
|
|
|
$pdat->{'bboxvalid'} = 0 ; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
_makeBand($cnvs) if($recr); |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
sub _inside { |
237
|
|
|
|
|
|
|
my($pbx1, $pby1, $pbx2, $pby2, $cbx1, $cby1, $cbx2, $cby2)= @_; |
238
|
|
|
|
|
|
|
my $wmrg = 0.01 * ($cbx2 - $cbx1); $wmrg = 3 if($wmrg < 3); # width margin |
239
|
|
|
|
|
|
|
my $hmrg = 0.01 * ($cby2 - $cby1); $hmrg = 3 if($hmrg < 3); # height margin |
240
|
|
|
|
|
|
|
return($pbx1 - $wmrg > $cbx1 && $pby1 - $hmrg > $cby1 && |
241
|
|
|
|
|
|
|
$pbx2 + $wmrg < $cbx2 && $pby2 + $hmrg < $cby2); |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
sub _new_bbox { |
244
|
|
|
|
|
|
|
my($cnvs)= @_; my $bwid = $cnvs->cget('-borderwidth'); my $pdat = $cnvs->privateData(); my($pbx1, $pby1, $pbx2, $pby2)= @{$pdat->{'bbox'}}; |
245
|
|
|
|
|
|
|
my $vwid = $cnvs->width() - 2 * $bwid; $pbx2++ if($pbx2 == $pbx1); my $zumx = $vwid / abs($pbx2 - $pbx1); |
246
|
|
|
|
|
|
|
my $vhit = $cnvs->height() - 2 * $bwid; $pby2++ if($pby2 == $pby1); my $zumy = $vhit / abs($pby2 - $pby1); |
247
|
|
|
|
|
|
|
my $zoom =($zumx > $zumy) ? $zumx : $zumy; |
248
|
|
|
|
|
|
|
if($zoom > 1.01) { _scale($cnvs, $cnvs->width() / 2, $cnvs->height() / 2, $zoom * 100) ; } |
249
|
|
|
|
|
|
|
my($cbx1, $cby1, $cbx2, $cby2)= _superBbox($cnvs, 'all'); |
250
|
|
|
|
|
|
|
$pdat->{ 'bbox' } = [$cbx1, $cby1, $cbx2, $cby2] ; |
251
|
|
|
|
|
|
|
$cnvs->configure('-scrollregion' => [$cbx1, $cby1, $cbx2, $cby2]); |
252
|
|
|
|
|
|
|
if($zoom > 1.01) { _scale($cnvs, $cnvs->width() / 2, $cnvs->height() / 2, 1 / ($zoom * 100)); } |
253
|
|
|
|
|
|
|
$pdat->{ 'bboxvalid'} = 1; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
sub _find_box { |
256
|
|
|
|
|
|
|
die "!*EROR*! The number of args to _find_box must be positive and even!\n" if((@_ % 2) || !@_); |
257
|
|
|
|
|
|
|
my($fbx1, $fbx2, $fby1, $fby2)=($_[0], $_[0], $_[1], $_[1]); |
258
|
|
|
|
|
|
|
for(my $indx = 2; $indx < @_; $indx += 2) { |
259
|
|
|
|
|
|
|
$fbx1 = $_[$indx ] if($_[$indx ] < $fbx1); |
260
|
|
|
|
|
|
|
$fbx2 = $_[$indx ] if($_[$indx ] > $fbx2); |
261
|
|
|
|
|
|
|
$fby1 = $_[$indx + 1] if($_[$indx + 1] < $fby1); |
262
|
|
|
|
|
|
|
$fby2 = $_[$indx + 1] if($_[$indx + 1] > $fby2); |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
return($fbx1, $fby1, $fbx2, $fby2); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
sub zoom { |
267
|
|
|
|
|
|
|
my($cnvs, $zoom)= @_; _new_bbox($cnvs) unless($cnvs->privateData->{'bboxvalid'}); _scale($cnvs, $cnvs->width() / 2, $cnvs->height() / 2, $zoom); |
268
|
|
|
|
|
|
|
$cnvs->Callback('-changeView' => getView($cnvs)) if(defined($cnvs->cget('-changeView'))); |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
sub _scale { |
271
|
|
|
|
|
|
|
my($cnvs, $xoff, $yoff, $scal)= @_; $scal = abs($scal); |
272
|
|
|
|
|
|
|
my $xval = $cnvs->canvasx(0) + $xoff; my $pdat = $cnvs->privateData(); |
273
|
|
|
|
|
|
|
my $yval = $cnvs->canvasy(0) + $yoff; return() unless($cnvs->type('all')); |
274
|
|
|
|
|
|
|
$pdat->{'movex'} = ($pdat->{'movex'} - $xval) * $scal + $xval; |
275
|
|
|
|
|
|
|
$pdat->{'movey'} = ($pdat->{'movey'} - $yval) * $scal + $yval; |
276
|
|
|
|
|
|
|
$pdat->{'scale'} *= $scal; $cnvs->SUPER::scale('all', $xval, $yval, $scal, $scal); |
277
|
|
|
|
|
|
|
my($pbx1, $pby1, $pbx2, $pby2)= @{$pdat->{'bbox'}}; |
278
|
|
|
|
|
|
|
$pbx1 = ($pbx1 - $xval) * $scal + $xval; $pbx2 = ($pbx2 - $xval) * $scal + $xval; |
279
|
|
|
|
|
|
|
$pby1 = ($pby1 - $yval) * $scal + $yval; $pby2 = ($pby2 - $yval) * $scal + $yval; |
280
|
|
|
|
|
|
|
$pdat->{ 'bbox'} = [$pbx1, $pby1, $pbx2, $pby2] ; |
281
|
|
|
|
|
|
|
$cnvs->configure('-scrollregion' => [$pbx1, $pby1, $pbx2, $pby2]); |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
sub center { |
284
|
|
|
|
|
|
|
my($cnvs, $xval, $yval)= @_; return() unless($cnvs->type('all')); |
285
|
|
|
|
|
|
|
my $pdat = $cnvs->privateData(); _new_bbox($cnvs) unless($pdat->{'bboxvalid'}); |
286
|
|
|
|
|
|
|
$xval = $xval * $pdat->{'scale'} + $pdat->{'movex'}; |
287
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { $yval = $yval * -$pdat->{'scale'} + $pdat->{'movey'}; } |
288
|
|
|
|
|
|
|
else { $yval = $yval * $pdat->{'scale'} + $pdat->{'movey'}; } |
289
|
|
|
|
|
|
|
my $xdlt = $cnvs->canvasx(0) + $cnvs->width() / 2 - $xval; $pdat->{'movex'} += $xdlt; |
290
|
|
|
|
|
|
|
my $ydlt = $cnvs->canvasy(0) + $cnvs->height() / 2 - $yval; $pdat->{'movey'} += $ydlt; |
291
|
|
|
|
|
|
|
$cnvs->SUPER::move('all', $xdlt, $ydlt); my($pbx1, $pby1, $pbx2, $pby2)= @{$pdat->{'bbox'}}; |
292
|
|
|
|
|
|
|
$pbx1 += $xdlt; $pbx2 += $xdlt; $pby1 += $ydlt; $pby2 += $ydlt; |
293
|
|
|
|
|
|
|
$pdat->{ 'bbox'} = [$pbx1, $pby1, $pbx2, $pby2] ; |
294
|
|
|
|
|
|
|
$cnvs->configure('-scrollregion' => [$pbx1, $pby1, $pbx2, $pby2]); |
295
|
|
|
|
|
|
|
$cnvs->Callback( '-changeView' => getView($cnvs)) if(defined($cnvs->cget('-changeView'))); |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
sub centerTags { my($cnvs, @args)= @_; my($cbx1, $cby1, $cbx2, $cby2)= bbox($cnvs, @args); return() unless(defined($cby2)); |
298
|
|
|
|
|
|
|
center($cnvs, ($cbx1 + $cbx2) / 2.0, ($cby1 + $cby2) / 2.0); |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
sub panAbstract { my($cnvs, $xval, $yval) = @_; |
301
|
|
|
|
|
|
|
my $cnvx = abstractx($cnvs, $cnvs->width() / 2) + $xval; |
302
|
|
|
|
|
|
|
my $cnvy = abstracty($cnvs, $cnvs->height() / 2) + $yval; center($cnvs, $cnvx, $cnvy); |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
sub viewAll { my $cnvs = shift(); return() unless($cnvs->type('all')); |
305
|
|
|
|
|
|
|
my %swch = ('-border' => 0.02, @_); $swch{'-border'} = 0 if($swch{'-border'} < 0); |
306
|
|
|
|
|
|
|
my $pdat = $cnvs->privateData(); _new_bbox($cnvs) unless($pdat->{'bboxvalid'}); |
307
|
|
|
|
|
|
|
my($pbx1, $pby1, $pbx2, $pby2)= @{$pdat->{'bbox'}}; my $scal = $pdat->{'scale'}; |
308
|
|
|
|
|
|
|
my $movx = $pdat->{'movex'}; my $movy = $pdat->{'movey'}; |
309
|
|
|
|
|
|
|
my $wnx1 = ($pbx1 - $movx) / $scal; my $wnx2 = ($pbx2 - $movx) / $scal; |
310
|
|
|
|
|
|
|
my $wny1 = ($pby1 - $movy) / $scal; my $wny2 = ($pby2 - $movy) / $scal; |
311
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { viewArea($cnvs, $wnx1,-$wny1, $wnx2,-$wny2, '-border' => $swch{'-border'}); } |
312
|
|
|
|
|
|
|
else { viewArea($cnvs, $wnx1, $wny1, $wnx2, $wny2, '-border' => $swch{'-border'}); } |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
sub viewArea { my($cnvs, $vwx1, $vwy1, $vwx2, $vwy2)= splice(@_, 0, 5); return() if(!defined($vwy2) || !$cnvs->type('all')); |
315
|
|
|
|
|
|
|
my %swch = ('-border' => 0.02, @_); $swch{'-border'} = 0 if($swch{'-border'} < 0); |
316
|
|
|
|
|
|
|
my $pdat = $cnvs->privateData(); _new_bbox($cnvs) unless($pdat->{'bboxvalid'}); |
317
|
|
|
|
|
|
|
($vwx1, $vwx2)=($vwx2, $vwx1) if($vwx1 > $vwx2); my $bwid = $swch{'-border'} * ($vwx2 - $vwx1); $vwx1 -= $bwid; $vwx2 += $bwid; |
318
|
|
|
|
|
|
|
($vwy1, $vwy2)=($vwy2, $vwy1) if($vwy1 > $vwy2); my $bhit = $swch{'-border'} * ($vwy2 - $vwy1); $vwy1 -= $bhit; $vwy2 += $bhit; |
319
|
|
|
|
|
|
|
my $scal = $pdat->{'scale'}; |
320
|
|
|
|
|
|
|
my $movx = $pdat->{'movex'}; my $cnvx = $cnvs->canvasx(0); my $cnx1 = $vwx1 * $scal + $movx - $cnvx; my $cnx2 = $vwx2 * $scal + $movx - $cnvx; |
321
|
|
|
|
|
|
|
my $movy = $pdat->{'movey'}; my $cnvy = $cnvs->canvasy(0); my $cny1 = $vwy1 * $scal + $movy - $cnvy; my $cny2 = $vwy2 * $scal + $movy - $cnvy; |
322
|
|
|
|
|
|
|
_view_area_canvas($cnvs, $cnx1, $cny1, $cnx2, $cny2); |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
sub _view_area_canvas { my($cnvs, $vwx1, $vwy1, $vwx2, $vwy2)= @_; return() unless($cnvs->type('all')); |
325
|
|
|
|
|
|
|
my $pdat = $cnvs->privateData(); _new_bbox($cnvs) unless($pdat->{'bboxvalid'}); |
326
|
|
|
|
|
|
|
my $bwid = $cnvs->cget('-borderwidth'); |
327
|
|
|
|
|
|
|
my $cwid = $cnvs->width(); my $dltx = $cwid / 2 - ($vwx1 + $vwx2) / 2; |
328
|
|
|
|
|
|
|
my $chit = $cnvs->height(); my $dlty = $chit / 2 - ($vwy1 + $vwy2) / 2; |
329
|
|
|
|
|
|
|
my $midx = $cnvs->canvasx(0) + $cwid / 2; $vwx2++ if($vwx2 == $vwx1); my $zumx = ($cwid - 2 * $bwid) / abs($vwx2 - $vwx1); |
330
|
|
|
|
|
|
|
my $midy = $cnvs->canvasy(0) + $chit / 2; $vwy2++ if($vwy2 == $vwy1); my $zumy = ($chit - 2 * $bwid) / abs($vwy2 - $vwy1); |
331
|
|
|
|
|
|
|
my $zoom = ($zumx < $zumy) ? $zumx : $zumy; |
332
|
|
|
|
|
|
|
if($zoom > 0.999 && $zoom < 1.001) { $cnvs->SUPER::move( 'all', $dltx, $dlty); } |
333
|
|
|
|
|
|
|
else { $cnvs->SUPER::scale('all', $midx - $dltx - $dltx / ($zoom - 1), $midy - $dlty - $dlty / ($zoom - 1), $zoom, $zoom); } |
334
|
|
|
|
|
|
|
$pdat->{'movex'} = ($pdat->{'movex'} + $dltx - $midx) * $zoom + $midx; |
335
|
|
|
|
|
|
|
$pdat->{'movey'} = ($pdat->{'movey'} + $dlty - $midy) * $zoom + $midy; |
336
|
|
|
|
|
|
|
$pdat->{'scale'} *= $zoom; my($pbx1, $pby1, $pbx2, $pby2) = @{$pdat->{'bbox'}}; |
337
|
|
|
|
|
|
|
$pbx1 = ($pbx1 + $dltx - $midx) * $zoom + $midx; $pbx2 = ($pbx2 + $dltx - $midx) * $zoom + $midx; |
338
|
|
|
|
|
|
|
$pby1 = ($pby1 + $dlty - $midy) * $zoom + $midy; $pby2 = ($pby2 + $dlty - $midy) * $zoom + $midy; |
339
|
|
|
|
|
|
|
$pdat->{ 'bbox'} = [$pbx1, $pby1, $pbx2, $pby2] ; |
340
|
|
|
|
|
|
|
$cnvs->configure('-scrollregion' => [$pbx1, $pby1, $pbx2, $pby2]); |
341
|
|
|
|
|
|
|
$cnvs->Callback( '-changeView' => getView($cnvs)) if(defined($cnvs->cget('-changeView'))); |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
sub _map_coords { my $cnvs = shift(); my @crds = (); my $chbx = 0; my $pdat = $cnvs->privateData(); my $xval = 1; |
344
|
|
|
|
|
|
|
my($pbx1, $pby1, $pbx2, $pby2)= @{$pdat->{'bbox'}}; |
345
|
|
|
|
|
|
|
my $movx = $pdat->{'movex'}; |
346
|
|
|
|
|
|
|
my $movy = $pdat->{'movey'}; |
347
|
|
|
|
|
|
|
my $scal = $pdat->{'scale'}; |
348
|
|
|
|
|
|
|
while(defined(my $argu = shift())) { |
349
|
|
|
|
|
|
|
if($argu !~ /^[+-.]*\d/) { |
350
|
|
|
|
|
|
|
unshift(@_, $argu); last(); |
351
|
|
|
|
|
|
|
} else { |
352
|
|
|
|
|
|
|
if($xval) { $argu = $argu * $scal + $movx; |
353
|
|
|
|
|
|
|
if($pbx2 < $pbx1) { $pbx2 = $pbx1 = $argu; $chbx = 1; } |
354
|
|
|
|
|
|
|
if($argu < $pbx1) { $pbx1 = $argu; $chbx = 1; } |
355
|
|
|
|
|
|
|
if($argu > $pbx2) { $pbx2 = $argu; $chbx = 1; } $xval = 0; |
356
|
|
|
|
|
|
|
} else { |
357
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { $argu = -$argu * $scal + $movy; } # invert y-coords |
358
|
|
|
|
|
|
|
else { $argu = $argu * $scal + $movy; } # don't invert y-coords |
359
|
|
|
|
|
|
|
if($pby2 < $pby1) { $pby2 = $pby1 = $argu; $chbx = 1; } |
360
|
|
|
|
|
|
|
if($argu < $pby1) { $pby1 = $argu; $chbx = 1; } |
361
|
|
|
|
|
|
|
if($argu > $pby2) { $pby2 = $argu; $chbx = 1; } $xval = 1; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
push(@crds, $argu); |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
if($chbx) { |
367
|
|
|
|
|
|
|
$pdat->{ 'bbox'} = [$pbx1, $pby1, $pbx2, $pby2] ; |
368
|
|
|
|
|
|
|
$cnvs->configure('-scrollregion' => [$pbx1, $pby1, $pbx2, $pby2]); |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
return(@crds, @_); |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
sub find { my($cnvs, @args)= @_; my $pdat = $cnvs->privateData(); |
373
|
|
|
|
|
|
|
if($args[0] =~ /^(closest|above|below)$/i) { |
374
|
|
|
|
|
|
|
if(lc($args[0]) eq 'closest' ) { return() if(@args < 3); |
375
|
|
|
|
|
|
|
my $scal = $pdat->{'scale' }; $args[1] = $args[1] * $scal + $pdat->{'movex'}; |
376
|
|
|
|
|
|
|
if( $pdat->{'inverty'}) { $args[2] = -$args[2] * $scal + $pdat->{'movey'}; } |
377
|
|
|
|
|
|
|
else { $args[2] = $args[2] * $scal + $pdat->{'movey'}; } |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
my $recr = _killBand($cnvs); my $foun = $cnvs->SUPER::find(@args); _makeBand($cnvs) if($recr); return($foun); |
380
|
|
|
|
|
|
|
} else { |
381
|
|
|
|
|
|
|
if($args[0] =~ /^(enclosed|overlapping)$/i) { return() if(@args < 5); |
382
|
|
|
|
|
|
|
my $scal = $pdat->{'scale' }; |
383
|
|
|
|
|
|
|
my $movx = $pdat->{'movex' }; $args[1] = $args[1] * $scal + $movx; $args[3] = $args[3] * $scal + $movx; |
384
|
|
|
|
|
|
|
my $movy = $pdat->{'movey' }; |
385
|
|
|
|
|
|
|
if( $pdat->{'inverty'}) { $args[2] = -$args[2] * $scal + $movy; $args[4] = -$args[4] * $scal + $movy; } |
386
|
|
|
|
|
|
|
else { $args[2] = $args[2] * $scal + $movy; $args[4] = $args[4] * $scal + $movy; } |
387
|
|
|
|
|
|
|
} |
388
|
|
|
|
|
|
|
my $recr = _killBand($cnvs); my @foun = $cnvs->SUPER::find(@args); _makeBand($cnvs) if($recr); return(@foun); |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
sub coords { my($cnvs, $tagg, @wcrd)= @_; return() unless($cnvs->type($tagg)); my $pdat = $cnvs->privateData(); |
392
|
|
|
|
|
|
|
my $scal = $pdat->{'scale'}; |
393
|
|
|
|
|
|
|
my $movx = $pdat->{'movex'}; |
394
|
|
|
|
|
|
|
my $movy = $pdat->{'movey'}; |
395
|
|
|
|
|
|
|
if(@wcrd) { |
396
|
|
|
|
|
|
|
die "!*EROR*! Missing y-coordinate in call to coords()!\n" if(@wcrd % 2); |
397
|
|
|
|
|
|
|
my($cbx1, $cby1, $cbx2, $cby2)= _find_box($cnvs->SUPER::coords($tagg)); my @ccrd = @wcrd; |
398
|
|
|
|
|
|
|
for(my $indx = 0; $indx < @ccrd; $indx += 2) { |
399
|
|
|
|
|
|
|
$ccrd[$indx ] = $ccrd[$indx ] * $scal + $movx; |
400
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { $ccrd[$indx + 1] = -$ccrd[$indx + 1] * $scal + $movy; } |
401
|
|
|
|
|
|
|
else { $ccrd[$indx + 1] = $ccrd[$indx + 1] * $scal + $movy; } |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
$cnvs->SUPER::coords($tagg, @ccrd); my($abx1, $aby1, $abx2, $aby2) = _find_box(@ccrd); |
404
|
|
|
|
|
|
|
_adjustBbox($cnvs, $cbx1, $cby1, $cbx2, $cby2, $abx1, $aby1, $abx2, $aby2); |
405
|
|
|
|
|
|
|
} else { |
406
|
|
|
|
|
|
|
@wcrd = $cnvs->SUPER::coords($tagg); |
407
|
|
|
|
|
|
|
die "!*EROR*! Missing y-coordinate in return value from SUPER::coords()!\n" if(@wcrd % 2); |
408
|
|
|
|
|
|
|
for(my $indx = 0; $indx < @wcrd; $indx += 2) { |
409
|
|
|
|
|
|
|
$wcrd[$indx ] = ($wcrd[$indx ] - $movx) / $scal; |
410
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { $wcrd[$indx + 1] = 0 - ($wcrd[$indx + 1] - $movy) / $scal; } |
411
|
|
|
|
|
|
|
else { $wcrd[$indx + 1] = ($wcrd[$indx + 1] - $movy) / $scal; } |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
if(@wcrd == 4 && ($wcrd[0] > $wcrd[2] || $wcrd[1] > $wcrd[3])) { |
414
|
|
|
|
|
|
|
my $type = $cnvs->type($tagg); |
415
|
|
|
|
|
|
|
if($type =~ /^(arc|oval|rectangle)$/) { |
416
|
|
|
|
|
|
|
($wcrd[0], $wcrd[2]) = ($wcrd[2], $wcrd[0]) if($wcrd[0] > $wcrd[2]); |
417
|
|
|
|
|
|
|
($wcrd[1], $wcrd[3]) = ($wcrd[3], $wcrd[1]) if($wcrd[1] > $wcrd[3]); |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
return(@wcrd); |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
return(); |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
sub scale { my($cnvs, $tagg, $xoff, $yoff, $xscl, $yscl) = @_; return() unless($cnvs->type($tagg)); my $pdat = $cnvs->privateData(); |
425
|
|
|
|
|
|
|
my $cnxo = $xoff * $pdat->{'scale'} + $pdat->{'movex'}; |
426
|
|
|
|
|
|
|
my $cnyo = $yoff * $pdat->{'scale'} + $pdat->{'movey'}; |
427
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { $cnyo = -$yoff * $pdat->{'scale'} + $pdat->{'movey'}; } |
428
|
|
|
|
|
|
|
if(lc($tagg) eq 'all') { $cnvs->SUPER::scale($tagg, $cnxo, $cnyo, $xscl, $yscl); my($pbx1, $pby1, $pbx2, $pby2) = @{$pdat->{'bbox'}}; |
429
|
|
|
|
|
|
|
$pbx1 = ($pbx1 - $cnxo) * $xscl + $cnxo; $pbx2 = ($pbx2 - $cnxo) * $xscl + $cnxo; |
430
|
|
|
|
|
|
|
$pby1 = ($pby1 - $cnyo) * $yscl + $cnyo; $pby2 = ($pby2 - $cnyo) * $yscl + $cnyo; |
431
|
|
|
|
|
|
|
$pdat->{ 'bbox'} = [$pbx1, $pby1, $pbx2, $pby2] ; |
432
|
|
|
|
|
|
|
$cnvs->configure('-scrollregion' => [$pbx1, $pby1, $pbx2, $pby2]); |
433
|
|
|
|
|
|
|
} else { |
434
|
|
|
|
|
|
|
my($cbx1, $cby1, $cbx2, $cby2) = _find_box($cnvs->SUPER::coords($tagg)); $cnvs->SUPER::scale($tagg, $cnxo, $cnyo, $xscl, $yscl); |
435
|
|
|
|
|
|
|
my $nwx1 = ($cbx1 - $cnxo) * $xscl + $cnxo; my $nwx2 = ($cbx2 - $cnxo) * $xscl + $cnxo; |
436
|
|
|
|
|
|
|
my $nwy1 = ($cby1 - $cnyo) * $yscl + $cnyo; my $nwy2 = ($cby2 - $cnyo) * $yscl + $cnyo; |
437
|
|
|
|
|
|
|
_adjustBbox($cnvs, $cbx1, $cby1, $cbx2, $cby2, $nwx1, $nwy1, $nwx2, $nwy2); |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
sub move { my($cnvs, $tagg, $xval, $yval)= @_; my($cbx1, $cby1, $cbx2, $cby2)= _find_box($cnvs->SUPER::coords($tagg)); |
441
|
|
|
|
|
|
|
my $scal = $cnvs->privateData->{'scale'}; my $dltx = $xval * $scal; my $dlty = $yval * $scal; $cnvs->SUPER::move($tagg, $dltx, $dlty); |
442
|
|
|
|
|
|
|
my($nwx1, $nwy1, $nwx2, $nwy2)= ($cbx1 + $dltx, $cby1 + $dlty, $cbx2 + $dltx, $cby2 + $dlty); |
443
|
|
|
|
|
|
|
_adjustBbox($cnvs, $cbx1, $cby1, $cbx2, $cby2, $nwx1, $nwy1, $nwx2, $nwy2); |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
sub _adjustBbox { my($cnvs, $cbx1, $cby1, $cbx2, $cby2, $nwx1, $nwy1, $nwx2, $nwy2)= @_; my $pdat = $cnvs->privateData(); |
446
|
|
|
|
|
|
|
my($pbx1, $pby1, $pbx2, $pby2) = @{$pdat->{'bbox'}}; my $chbx = 0; |
447
|
|
|
|
|
|
|
if($nwx1 < $pbx1) { $pbx1 = $nwx1; $chbx = 1; } if($nwx2 > $pbx2) { $pbx2 = $nwx2; $chbx = 1; } |
448
|
|
|
|
|
|
|
if($nwy1 < $pby1) { $pby1 = $nwy1; $chbx = 1; } if($nwy2 > $pby2) { $pby2 = $nwy2; $chbx = 1; } |
449
|
|
|
|
|
|
|
if($chbx) { |
450
|
|
|
|
|
|
|
$pdat->{ 'bbox'} = [$pbx1, $pby1, $pbx2, $pby2] ; |
451
|
|
|
|
|
|
|
$cnvs->configure('-scrollregion' => [$pbx1, $pby1, $pbx2, $pby2]); |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
my $wmrg = 0.01 * ($pbx2 - $pbx1); $wmrg = 3 if($wmrg < 3); # width margin |
454
|
|
|
|
|
|
|
my $hmrg = 0.01 * ($pby2 - $pby1); $hmrg = 3 if($hmrg < 3); # height margin |
455
|
|
|
|
|
|
|
if(($cbx1 - $wmrg < $pbx1 && $cbx1 < $nwx1) || ($cby1 - $hmrg < $pby1 && $cby1 < $nwy1) || |
456
|
|
|
|
|
|
|
($cbx2 + $wmrg > $pbx2 && $cbx2 > $nwx2) || ($cby2 + $hmrg > $pby2 && $cby2 > $nwy2)) { $pdat->{'bboxvalid'} = 0; } |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
sub bbox { my $cnvs = shift(); my $xact = 0; if($_[0] =~ /-exact/i) { shift(); $xact = shift(); } my @tags = @_; my $foun = 0; |
459
|
|
|
|
|
|
|
for(@tags) { if($cnvs->type($_)) { $foun = 1; last(); } } return() unless($foun); my $pdat = $cnvs->privateData(); |
460
|
|
|
|
|
|
|
if(lc($tags[0]) eq 'all') { |
461
|
|
|
|
|
|
|
my($pbx1, $pby1, $pbx2, $pby2)= @{$pdat->{'bbox'}}; |
462
|
|
|
|
|
|
|
my $scal = $pdat->{'scale'}; |
463
|
|
|
|
|
|
|
my $movx = $pdat->{'movex'}; |
464
|
|
|
|
|
|
|
my $movy = $pdat->{'movey'}; |
465
|
|
|
|
|
|
|
my $wnx1 = ($pbx1 - $movx) / $scal; my $wnx2 = ($pbx2 - $movx) / $scal; |
466
|
|
|
|
|
|
|
my $wny1 = ($pby1 - $movy) / $scal; my $wny2 = ($pby2 - $movy) / $scal; |
467
|
|
|
|
|
|
|
($wnx1, $wnx2) = ($wnx2, $wnx1) if($wnx2 < $wnx1); |
468
|
|
|
|
|
|
|
($wny1, $wny2) = ($wny2, $wny1) if($wny2 < $wny1); |
469
|
|
|
|
|
|
|
return($wnx1, $wny1, $wnx2, $wny2); |
470
|
|
|
|
|
|
|
} else { |
471
|
|
|
|
|
|
|
my $onep = 1.0 / $pdat->{'scale'}; my $zfix = 0; # one pixel; zoom fix |
472
|
|
|
|
|
|
|
if($xact && $onep > 0.001) { zoom($cnvs, $onep * 1000); $zfix = 1; } |
473
|
|
|
|
|
|
|
my($cbx1, $cby1, $cbx2, $cby2)= _superBbox($cnvs, @tags); |
474
|
|
|
|
|
|
|
unless(defined($cbx1)) { zoom($cnvs, 1 / ($onep * 1000)) if($zfix); return(); } # @tags exist but their bbox overflows as ints |
475
|
|
|
|
|
|
|
if(!$xact && abs($cbx2 - $cbx1) < 27 && abs($cby2 - $cby1) < 27) { # if error looks to be greater than certain %, do exact anyway |
476
|
|
|
|
|
|
|
zoom($cnvs, $onep * 1000); my($nwx1, $nwy1, $nwx2, $nwy2)= _superBbox($cnvs, @tags); |
477
|
|
|
|
|
|
|
if( !defined($nwx1)) { zoom($cnvs, 1 / ($onep * 1000)); } # overflows ints so retreat to previous box |
478
|
|
|
|
|
|
|
else { $zfix = 1; ($cbx1, $cby1, $cbx2, $cby2)=($nwx1, $nwy1, $nwx2, $nwy2); } |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
my $scal = $pdat->{'scale' }; |
481
|
|
|
|
|
|
|
my $movx = $pdat->{'movex' }; $cbx1 = ($cbx1 - $movx) / $scal; $cbx2 = ($cbx2 - $movx) / $scal; |
482
|
|
|
|
|
|
|
my $movy = $pdat->{'movey' }; |
483
|
|
|
|
|
|
|
if( $pdat->{'inverty'}) { $cby1 = ($cby1 - $movy) / -$scal; $cby2 = ($cby2 - $movy) / -$scal; } |
484
|
|
|
|
|
|
|
else { $cby1 = ($cby1 - $movy) / $scal; $cby2 = ($cby2 - $movy) / $scal; } |
485
|
|
|
|
|
|
|
zoom($cnvs, 1 / ($onep * 1000)) if($zfix); |
486
|
|
|
|
|
|
|
if( $pdat->{'inverty'}) { return($cbx1, $cby2, $cbx2, $cby1); } |
487
|
|
|
|
|
|
|
else { return($cbx1, $cby1, $cbx2, $cby2); } |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
sub rubberBand { |
491
|
|
|
|
|
|
|
die "!*EROR*! Wrong number of args passed to rubberBand()!\n" unless(@_ == 2); my($cnvs, $step)= @_; my $pdat = $cnvs->privateData(); |
492
|
|
|
|
|
|
|
return() if($step >= 1 && !defined($pdat->{'RubberBand'})); my $xevt = $cnvs->XEvent(); |
493
|
|
|
|
|
|
|
my $xabs = abstractx($cnvs, $xevt->x()); |
494
|
|
|
|
|
|
|
my $yabs = abstracty($cnvs, $xevt->y()); |
495
|
|
|
|
|
|
|
if ($step == 0) { _killBand($cnvs); $pdat->{'RubberBand'} = [$xabs, $yabs, $xabs, $yabs]; # create anchor for rubberband |
496
|
|
|
|
|
|
|
} elsif($step == 1) { $pdat->{'RubberBand'}[2] = $xabs; $pdat->{'RubberBand'}[3] = $yabs; _killBand($cnvs); _makeBand($cnvs); # updt end of band && redraw |
497
|
|
|
|
|
|
|
} elsif($step == 2) { _killBand($cnvs) || return(); my($pbx1, $pby1, $pbx2, $pby2) = @{$pdat->{'RubberBand'}}; undef($pdat->{'RubberBand'}); # done |
498
|
|
|
|
|
|
|
($pbx1, $pbx2) = ($pbx2, $pbx1) if($pbx2 < $pbx1); |
499
|
|
|
|
|
|
|
($pby1, $pby2) = ($pby2, $pby1) if($pby2 < $pby1); return($pbx1, $pby1, $pbx2, $pby2); |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
sub _superBbox { my($cnvs, @tags)= @_; my $recr = _killBand($cnvs); |
503
|
|
|
|
|
|
|
my($cbx1, $cby1, $cbx2, $cby2)= $cnvs->SUPER::bbox(@tags); _makeBand($cnvs) if($recr); return($cbx1, $cby1, $cbx2, $cby2); |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
sub _killBand { my($cnvs)= @_; my $rbid = $cnvs->privateData->{'RubberBandID'}; return(0) unless(defined($rbid)); $cnvs->SUPER::delete($rbid); |
506
|
|
|
|
|
|
|
undef($cnvs->privateData->{'RubberBandID'}); return(1); |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
sub _makeBand { my($cnvs)= @_; my $pdat = $cnvs->privateData(); my $rbnd = $pdat->{'RubberBand'}; |
509
|
|
|
|
|
|
|
die "!*EROR*! RubberBand is not defined!" unless(defined($rbnd)); |
510
|
|
|
|
|
|
|
die "!*EROR*! RubberBand does not have 4 values!" if(@$rbnd != 4); |
511
|
|
|
|
|
|
|
my $scal = $pdat->{'scale'}; my $colr = $cnvs->cget('-bandColor'); |
512
|
|
|
|
|
|
|
my $movx = $pdat->{'movex'}; my $rbx1 = $rbnd->[0] * $scal + $movx; my $rbx2 = $rbnd->[2] * $scal + $movx; |
513
|
|
|
|
|
|
|
my $movy = $pdat->{'movey'}; my $rby1 = $rbnd->[1] * $scal + $movy; my $rby2 = $rbnd->[3] * $scal + $movy; |
514
|
|
|
|
|
|
|
my $rbid = $cnvs->SUPER::create('rectangle', $rbx1, $rby1, $rbx2, $rby2, '-outline' => $colr); $pdat->{'RubberBandID'} = $rbid; |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
sub eventLocation { my($cnvs)= @_; my $xevt = $cnvs->XEvent(); return($cnvs->abstractx($xevt->x()),$cnvs->abstracty($xevt->y())) if(defined($xevt)); return();} |
517
|
|
|
|
|
|
|
sub viewFit { my $cnvs = shift(); my $bord = 0.02; |
518
|
|
|
|
|
|
|
if(lc($_[0]) eq '-border') { shift(); $bord = shift() if(@_); $bord = 0 if($bord < 0); } |
519
|
|
|
|
|
|
|
my @tags = @_; my $foun = 0; |
520
|
|
|
|
|
|
|
for(@tags) { if($cnvs->type($_)) { $foun = 1; last(); } } return() unless($foun); |
521
|
|
|
|
|
|
|
viewArea($cnvs, bbox($cnvs, @tags), '-border' => $bord); |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
sub pixelSize { my($cnvs)= @_; return(1.0 / $cnvs->privateData->{'scale'}); } |
524
|
|
|
|
|
|
|
sub abstractx { my($cnvs, $xval )= @_; my $pdat = $cnvs->privateData(); my $scal = $pdat->{'scale'}; return() unless($scal); |
525
|
|
|
|
|
|
|
return( ($cnvs->canvasx(0) + $xval - $pdat->{'movex'}) / $scal); |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
sub abstracty { my($cnvs, $yval)= @_; my $pdat = $cnvs->privateData(); my $scal = $pdat->{'scale'}; return() unless($scal); |
528
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { return(0 - ($cnvs->canvasy(0) + $yval - $pdat->{'movey'}) / $scal); } |
529
|
|
|
|
|
|
|
else { return( ($cnvs->canvasy(0) + $yval - $pdat->{'movey'}) / $scal); } |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
sub abstractxy { my($cnvs, $xval, $yval)= @_; my $pdat = $cnvs->privateData(); my $scal = $pdat->{'scale'}; return() unless($scal); |
532
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { return( ($cnvs->canvasx(0) + $xval - $pdat->{'movex'}) / $scal, |
533
|
|
|
|
|
|
|
0 - ($cnvs->canvasy(0) + $yval - $pdat->{'movey'}) / $scal); } |
534
|
|
|
|
|
|
|
else { return( ($cnvs->canvasx(0) + $xval - $pdat->{'movex'}) / $scal, |
535
|
|
|
|
|
|
|
($cnvs->canvasy(0) + $yval - $pdat->{'movey'}) / $scal); } |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
sub widgetx { my($cnvs, $xval )= @_; my $pdat = $cnvs->privateData(); |
538
|
|
|
|
|
|
|
return( $xval * $pdat->{'scale'} + $pdat->{'movex'} - $cnvs->canvasx(0)); |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
sub widgety { my($cnvs, $yval)= @_; my $pdat = $cnvs->privateData(); |
541
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { return(-$yval * $pdat->{'scale'} + $pdat->{'movey'} - $cnvs->canvasy(0)); } |
542
|
|
|
|
|
|
|
else { return( $yval * $pdat->{'scale'} + $pdat->{'movey'} - $cnvs->canvasy(0)); } |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
sub widgetxy { my($cnvs, $xval, $yval)= @_; my $pdat = $cnvs->privateData(); my $scal = $pdat->{'scale'}; |
545
|
|
|
|
|
|
|
if($pdat->{'inverty'}) { return ( $xval * $scal + $pdat->{'movex'} - $cnvs->canvasx(0), |
546
|
|
|
|
|
|
|
-$yval * $scal + $pdat->{'movey'} - $cnvs->canvasy(0)); } |
547
|
|
|
|
|
|
|
else { return ( $xval * $scal + $pdat->{'movex'} - $cnvs->canvasx(0), |
548
|
|
|
|
|
|
|
$yval * $scal + $pdat->{'movey'} - $cnvs->canvasy(0)); } |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
my $cmap = 0; # global cmap flag is used to avoid calling _map_coords twice |
551
|
|
|
|
|
|
|
sub create { my($cnvs, $type)= splice(@_, 0, 2); my @narg = ($cmap) ? @_ : _map_coords($cnvs, @_); |
552
|
|
|
|
|
|
|
if ($type eq 'rectangle') { $cnvs->_rect_to_poly( @narg); } |
553
|
|
|
|
|
|
|
elsif($type eq 'oval') { $cnvs->_oval_to_poly( @narg); } |
554
|
|
|
|
|
|
|
else { $cnvs->SUPER::create($type, @narg); } |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
sub createPolygon { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $plid = $cnvs->SUPER::createPolygon( @narg); $cmap = 0;return($plid);} |
557
|
|
|
|
|
|
|
sub createLine { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $lnid = $cnvs->SUPER::createLine( @narg); $cmap = 0;return($lnid);} |
558
|
|
|
|
|
|
|
sub createText { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $txid = $cnvs->SUPER::createText( @narg); $cmap = 0;return($txid);} |
559
|
|
|
|
|
|
|
sub createWindow { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $wnid = $cnvs->SUPER::createWindow( @narg); $cmap = 0;return($wnid);} |
560
|
|
|
|
|
|
|
sub createBitmap { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $bmid = $cnvs->SUPER::createBitmap( @narg); $cmap = 0;return($bmid);} |
561
|
|
|
|
|
|
|
sub createImage { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $imid = $cnvs->SUPER::createImage( @narg); $cmap = 0;return($imid);} |
562
|
|
|
|
|
|
|
sub createArc { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $arid = $cnvs->SUPER::createArc( @narg); $cmap = 0;return($arid);} |
563
|
|
|
|
|
|
|
sub createOval { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $ovid; |
564
|
|
|
|
|
|
|
if($cnvs->privateData->{'oval_to_poly'}) { $ovid = $cnvs->_oval_to_poly( @narg);} |
565
|
|
|
|
|
|
|
else { $ovid = $cnvs->SUPER::createOval( @narg);}$cmap = 0;return($ovid); |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
sub createRectangle { my $cnvs = shift; my @narg = _map_coords($cnvs,@_); $cmap = 1; my $rcid; |
568
|
|
|
|
|
|
|
if($cnvs->privateData->{'rect_to_poly'}) { $rcid = $cnvs->_rect_to_poly( @narg);} |
569
|
|
|
|
|
|
|
else { $rcid = $cnvs->SUPER::createRectangle(@narg);}$cmap = 0;return($rcid); |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
sub _rect_to_poly { my $self = shift(); my($left, $topp, $rite, $botm)= splice(@_, 0, 4); # transform rectangle coords into poly coords |
572
|
|
|
|
|
|
|
($left, $rite)=($rite, $left) if($rite < $left); |
573
|
|
|
|
|
|
|
($topp, $botm)=($botm, $topp) if($botm < $topp); |
574
|
|
|
|
|
|
|
$self->createPolygon($left, $topp, $rite, $topp, $rite, $botm, $left, $botm, @_); |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
sub _oval_to_poly { my $self = shift(); my($left, $topp, $rite, $botm)= splice(@_, 0, 4); my $stps = 127; # default steps in poly approx of oval |
577
|
|
|
|
|
|
|
if(lc($_[0]) eq '-steps') { shift(); $stps = shift(); } # or can be configured |
578
|
|
|
|
|
|
|
my $xcnt = ($rite - $left) / 2; |
579
|
|
|
|
|
|
|
my $ycnt = ($botm - $topp) / 2; my @ptls; |
580
|
|
|
|
|
|
|
for my $indx (0..$stps) { my $thta = (PI * 2) * ($indx / $stps); |
581
|
|
|
|
|
|
|
my $xnew = $xcnt * cos($thta) - $xcnt + $rite; |
582
|
|
|
|
|
|
|
my $ynew = $ycnt * sin($thta) + $ycnt + $topp; push(@ptls, $xnew, $ynew); |
583
|
|
|
|
|
|
|
} |
584
|
|
|
|
|
|
|
push(@_, '-fill' , undef ) unless(grep {/-fill/ } @_); |
585
|
|
|
|
|
|
|
push(@_, '-outline', 'black') unless(grep {/-outline/} @_); $self->createPolygon(@ptls, @_); |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
sub _get_CM { my($xcnt, $ycnt, $area); my $indx = 0; # find Center of Mass of polygon |
588
|
|
|
|
|
|
|
while($indx < $#_) { my $xzer = $_[$indx ]; my $yzer = $_[$indx + 1]; my($xone, $yone); |
589
|
|
|
|
|
|
|
if($indx + 2 > $#_) { $xone = $_[ 0]; $yone = $_[ 1]; } |
590
|
|
|
|
|
|
|
else { $xone = $_[$indx + 2]; $yone = $_[$indx + 3]; } |
591
|
|
|
|
|
|
|
$indx += 2; my $aone = ($xzer + $xone) / 2; my $atwo = ($xzer**2 + $xzer*$xone + $xone**2 ) / 6; |
592
|
|
|
|
|
|
|
my $ydlt = $yone - $yzer; my $athr = ($xzer*$yone + $yzer*$xone + 2 * ($xone*$yone + $xzer*$yzer)) / 6; |
593
|
|
|
|
|
|
|
$area += $aone * $ydlt; $xcnt += $atwo * $ydlt; $ycnt += $athr * $ydlt; |
594
|
|
|
|
|
|
|
} |
595
|
|
|
|
|
|
|
return(split(' ', sprintf("%.0f %0.f" => $xcnt/$area, $ycnt/$area))); |
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
8; |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=encoding utf8 |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=head1 NAME |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
Tk::AbstractCanvas - Canvas with Abstract center, zoom, and rotate methods |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=head1 VERSION |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
This documentation refers to version 1.8 of Tk::AbstractCanvas, which was released on |
608
|
|
|
|
|
|
|
Sun Jul 9 09:27:50:55 -0500 2017. |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=head1 SYNOPSIS |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
use Tk; |
613
|
|
|
|
|
|
|
use Tk::AbstractCanvas; |
614
|
|
|
|
|
|
|
my $mwin = Tk::MainWindow->new(); |
615
|
|
|
|
|
|
|
my $acnv = $mwin->AbstractCanvas()->pack('-expand' => 1, |
616
|
|
|
|
|
|
|
'-fill' => 'both'); |
617
|
|
|
|
|
|
|
#$acnv->invertY( 1); # uncomment for inverted y-axis |
618
|
|
|
|
|
|
|
$acnv->controlNav(1); # advanced CtrlKey+MouseDrag Navigation |
619
|
|
|
|
|
|
|
$acnv->rectToPoly(1); |
620
|
|
|
|
|
|
|
#$acnv->ovalToPoly(1); # uncomment for oval to rot8 with canvas |
621
|
|
|
|
|
|
|
my $rect = $acnv->createRectangle( 7, 8, 24, 23, '-fill' => 'red'); |
622
|
|
|
|
|
|
|
my $oval = $acnv->createOval( 23, 24, 32, 27, '-fill' => 'green'); |
623
|
|
|
|
|
|
|
my $line = $acnv->createLine( 0, 1, 31, 32, '-fill' => 'blue', |
624
|
|
|
|
|
|
|
'-arrow' => 'last'); |
625
|
|
|
|
|
|
|
my $labl = $mwin->Label('-text' => 'Hello AbstractCanvas! =)'); |
626
|
|
|
|
|
|
|
my $wind = $acnv->createWindow(15, 16, '-window' => $labl ); |
627
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { $acnv->zoom(1.04); }); |
628
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { $acnv->zoom(0.97); }); |
629
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
630
|
|
|
|
|
|
|
$acnv->rotate($rect, 5); |
631
|
|
|
|
|
|
|
$acnv->rotate($wind, 5); # this rot8 should do nothing because |
632
|
|
|
|
|
|
|
$acnv->rotate($oval, -5); # window can't go around own center |
633
|
|
|
|
|
|
|
$acnv->rotate($line, -5); }); |
634
|
|
|
|
|
|
|
$acnv->viewAll(); |
635
|
|
|
|
|
|
|
MainLoop(); # ... then click the 3 mouse buttons |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=head1 DESCRIPTION |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
AbstractCanvas provides an alternative to a L object which partially abstracts the coordinates of objects drawn onto itself. This allows the |
640
|
|
|
|
|
|
|
entire Canvas to be zoomed or rotated. Rotations modify the coordinates that the original object was placed at but zooming the whole canvas does not. |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
Tk::AbstractCanvas is derived from the excellent modules L by Joseph Skrovan (which was itself based on a version by Rudy |
643
|
|
|
|
|
|
|
Albachten ) and L by Ala Qumsieh . |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=head1 USAGE |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=head1 DESCRIPTION |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
This module is a wrapper around the Canvas widget that maps the user's coordinate system to the now mostly hidden coordinate system of the Canvas |
650
|
|
|
|
|
|
|
widget. There is an option to make the abstract coordinates y-axis increase in the upward direction rather than the default downward. |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
I is meant to be a useful alternative to a regular Canvas. Typically, you should call $acnv->viewAll() (or |
653
|
|
|
|
|
|
|
$acnv->viewArea(@box)) before calling MainLoop(). |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
Most of the I methods are the same as regular I |
656
|
|
|
|
|
|
|
coordinates. |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
I also adds a new rotate() method to allow rotation of canvas objects by arbitrary angles. |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=head1 NEW METHODS |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=over 2 |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
Zooms the display by the specified amount. Example: |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub {$acnv->zoom(1.25)}); |
669
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub {$acnv->zoom(0.8 )}); |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
# If you are using the 'Scrolled' constructor as in: |
672
|
|
|
|
|
|
|
my $acnv = $mwin->Scrolled('AbstractCanvas', -scrollbars => 'nw',); # ... |
673
|
|
|
|
|
|
|
# you want to bind the key-presses to the 'AbstractCanvas' Subwidget of Scrolled. |
674
|
|
|
|
|
|
|
my $scrolled_canvas = $acnv->Subwidget('abstractcanvas'); # note the lowercase |
675
|
|
|
|
|
|
|
$scrolled_canvas->CanvasBind('' => sub {$scrolled_canvas->zoom(1.25)}); |
676
|
|
|
|
|
|
|
$scrolled_canvas->CanvasBind('' => sub {$scrolled_canvas->zoom(0.8 )}); |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
# If you don't like the scrollbars taking the focus when you |
679
|
|
|
|
|
|
|
# -tab through the windows, you can: |
680
|
|
|
|
|
|
|
$acnv->Subwidget('xscrollbar')->configure(-takefocus => 0); |
681
|
|
|
|
|
|
|
$acnv->Subwidget('yscrollbar')->configure(-takefocus => 0); |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
Centers the display around abstract coordinates x, y. Example: |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
$acnv->CanvasBind('<2>' => sub { |
688
|
|
|
|
|
|
|
$acnv->CanvasFocus(); |
689
|
|
|
|
|
|
|
$acnv->center($acnv->eventLocation()); |
690
|
|
|
|
|
|
|
}); |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=item I<$acnv>->B([-exact => {0 | 1}], I) |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
Centers the display around the center of the bounding box containing the specified TagOrIDs without changing the current magnification of the display. |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
'-exact => 1' will cause the canvas to be scaled twice to get an accurate bounding box. This will be an expensive computation if the canvas contains a |
697
|
|
|
|
|
|
|
large number of objects. |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=item I<$acnv>->B() |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
Returns the abstract coordinates (x, y) of the last Xevent. |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
Pans the display by the specified abstract distances. B is not meant to replace the xview/yview panning methods. Most user interfaces |
706
|
|
|
|
|
|
|
will want the arrow keys tied to the xview/yview panning methods (the default bindings), which pan in widget coordinates. |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
If you do want to change the arrow key-bindings to pan in abstract coordinates using B you must disable the default arrow key-bindings. Example: |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
$mwin->bind('AbstractCanvas', '' => ''); |
711
|
|
|
|
|
|
|
$mwin->bind('AbstractCanvas', '' => ''); |
712
|
|
|
|
|
|
|
$mwin->bind('AbstractCanvas', '' => ''); |
713
|
|
|
|
|
|
|
$mwin->bind('AbstractCanvas', '' => ''); |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => sub {$acnv->panAbstract(0, 100)}); |
716
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => sub {$acnv->panAbstract(0, -100)}); |
717
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => sub {$acnv->panAbstract(-100, 0)}); |
718
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub {$acnv->panAbstract( 100, 0)}); |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
This is not usually desired, as the percentage of the display that is shifted will be dependent on the current display magnification. |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
Returns the state of whether the y-axis of the abstract coordinate system is inverted. The default of this value is 0. An optional parameter can be |
725
|
|
|
|
|
|
|
supplied to set the value. |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
Returns the state of whether created rectangles should be auto-converted into polygons (so that they can be rotated about their center by the rotate() |
730
|
|
|
|
|
|
|
method). The default of this value is 0. An optional parameter can be supplied to set the value. |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
Returns the state of whether created ovals should be auto-converted into polygons (so that they can be rotated about their center by the rotate() |
735
|
|
|
|
|
|
|
method). The default of this value is 0. An optional parameter can be supplied to set the value. |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
Returns the state of whether special Control+MouseButton drag navigation bindings are set. When true, Control-Button-1 mouse dragging rotates the |
740
|
|
|
|
|
|
|
whole AbstractCanvas, 2 pans, and 3 zooms. The default of this value is 0 but this option is very useful if you don't need Control-Button bindings for some |
741
|
|
|
|
|
|
|
other purpose. An optional parameter can be supplied to set the value. |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
Returns the state of whether special Control+MouseButton actions are busy handling events. An optional parameter can be supplied to set the value. |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
Returns the value of the special controlNav zoom scale (activated by Control-Button-3 dragging). The default value is -0.001. The zoom function |
750
|
|
|
|
|
|
|
takes the distance dragged in pixels across the positive x and y axes scaled by the zoom factor to determine the zoom result. If you make the scale |
751
|
|
|
|
|
|
|
positive, it will invert the directions which zoom in and out. If you make the number larger (e.g., -0.003 or 0.003), zooming will become more |
752
|
|
|
|
|
|
|
twitchy. If you make the number smaller (e.g., -0.0007 or 0.0007), zooming will happen more smoothly. An optional parameter can be supplied to |
753
|
|
|
|
|
|
|
set the value. |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
Returns the value of the special controlNav rotation scale (activated by Control-Button-1 dragging). The default value is -0.3. The rotation function |
758
|
|
|
|
|
|
|
takes the distance dragged in pixels across the positive x and y axes scaled by the rotation factor to determine the rotation result. If you make the |
759
|
|
|
|
|
|
|
scale positive, it will invert the directions which rotate positive or negative degrees. If you make the number larger (e.g., -0.7 or 0.7), rotations |
760
|
|
|
|
|
|
|
will become more twitchy. If you make the number smaller (e.g., -0.07 or 0.07), rotations will happen more smoothly. An optional parameter can be |
761
|
|
|
|
|
|
|
supplied to set the value. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=item I<$acnv>-EB([\&new_callback]) |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
Returns the value of the special controlNav rotation motion callback. This will let a user tidy up whatever coordinates are necessary to keep sub-groups |
766
|
|
|
|
|
|
|
of widgets in certain orientations together while the whole canvas is rotated. An optional parameter can be supplied to set the value. |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
=item I<$acnv>-EB([\&new_callback]) |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
Returns the value of the special controlNav rotation release callback. This will let a user tidy up whatever coordinates are necessary to keep sub-groups |
771
|
|
|
|
|
|
|
of widgets in certain orientations together after the whole canvas is done being rotated. An optional parameter can be supplied to set the value. |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
Returns the scale value of the AbstractCanvas relative to the underlying canvas. An optional parameter can be supplied to set the value although the zoom |
776
|
|
|
|
|
|
|
function should almost always be employed instead of manipulating the scale directly through this accessor. |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
Returns the x-coordinate of where the last special Control+MouseButton event occurred. An optional parameter can be supplied to set the value. |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
=item I<$acnv>-EB([new_value]) |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
Returns the y-coordinate of where the last special Control+MouseButton event occurred. An optional parameter can be supplied to set the value. |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=item I<$acnv>-EB(I ?,I?) |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
This method rotates the object identified by TagOrID by I. The angle is specified in I. If an I coordinate is specified, then |
789
|
|
|
|
|
|
|
the object is rotated about that point. Otherwise, the object is rotated about its center point, if that can be determined. |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
=item I<$acnv>->B() |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
Returns the width (in abstract coordinates) of a pixel (at the current magnification). |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=item I<$acnv>->B(I<{0|1|2}>) |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
Creates a rubber banding box that allows the user to graphically select a region. B is called with a step parameter '0', '1', or '2'. |
798
|
|
|
|
|
|
|
'0' to start a new box, '1' to stretch the box, and '2' to finish the box. When called with '2', the specified box is returned (x1, y1, x2, y2) |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
The band color is set with the I option '-bandColor'. The default color is 'red'. Example specifying a region to delete: |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
$acnv->configure(-bandColor => 'purple'); |
803
|
|
|
|
|
|
|
$acnv->CanvasBind('<3>' => sub {$acnv->CanvasFocus; |
804
|
|
|
|
|
|
|
$acnv->rubberBand(0)}); |
805
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub {$acnv->rubberBand(1)}); |
806
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { |
807
|
|
|
|
|
|
|
my @box = $acnv->rubberBand(2); |
808
|
|
|
|
|
|
|
my @ids = $acnv->find('enclosed', @box); |
809
|
|
|
|
|
|
|
for my $id (@ids) {$acnv->delete($id)} }); |
810
|
|
|
|
|
|
|
# Note: '' will be called for any ButtonRelease! |
811
|
|
|
|
|
|
|
# Use '' instead. |
812
|
|
|
|
|
|
|
# If you want the rubber band to look smooth during panning and zooming, add |
813
|
|
|
|
|
|
|
# rubberBand(1) update calls to the appropriate key-bindings: |
814
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => sub { $acnv->rubberBand(1)}); |
815
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => sub { $acnv->rubberBand(1)}); |
816
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => sub { $acnv->rubberBand(1)}); |
817
|
|
|
|
|
|
|
$acnv->CanvasBind('' => sub { $acnv->rubberBand(1)}); |
818
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => sub {$acnv->zoom(1.25); $acnv->rubberBand(1)}); |
819
|
|
|
|
|
|
|
$acnv->CanvasBind( '' => sub {$acnv->zoom(0.8 ); $acnv->rubberBand(1)}); |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
This box avoids the overhead of bounding box calculations that can occur if you create your own rubberBand outside of I. |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
=item I<$acnv>->B([-border => number]) |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
Displays at maximum possible zoom all objects centered in the I. The switch '-border' specifies, as a percentage of the screen, the minimum |
826
|
|
|
|
|
|
|
amount of white space to be left on the edges of the display. Default '-border' is 0.02. |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=item I<$acnv>->B(x1, y1, x2, y2, [-border => number])) |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
Displays at maximum possible zoom the specified region centered in the I. |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
=item I<$acnv>->B([-border => number], I, [I, ...]) |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
Adjusts the AbstractCanvas to display all of the specified tags. The '-border' switch specifies (as a percentage) how much extra surrounding space should be shown. |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
=item I<$acnv>->B() |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
Returns the rectangle of the current view (x1, y1, x2, y2) |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
Convert abstract coordinates to widget coordinates. |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
=item I<$acnv>->B(I) |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
Convert widget coordinates to abstract coordinates. |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
=back |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
=head1 CHANGED METHODS |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
Abstract coordinates are supplied and returned to B methods instead of widget coordinates unless otherwise specified. (i.e., These methods take |
861
|
|
|
|
|
|
|
and return abstract coordinates: center, panAbstract, viewArea, find, coords, scale, move, bbox, rubberBand, eventLocation, pixelSize, and create*) |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
=over 2 |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=item I<$acnv>->B([-exact => {0 | 1}], I, [I, ...]) |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
'-exact => 1' is only needed if the TagOrID is not 'all'. It will cause the canvas to be scaled twice to get an accurate bounding box. This will be |
868
|
|
|
|
|
|
|
expensive computationally if the canvas contains a large number of objects. |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
Neither setting of exact will produce exact results because the underlying canvas bbox method returns a slightly larger box to insure that everything is |
871
|
|
|
|
|
|
|
contained. It appears that a number close to '2' is added or subtracted. The '-exact => 1' zooms in to reduce this error. |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
If the underlying canvas B method returns a bounding box that is small (high error percentage) then '-exact => 1' is done automatically. |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
=item I<$acnv>->B(I<'all', xOrigin, yOrigin, xScale, yScale>) |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
B should not be used to 'zoom' the display in and out as it will change the abstract coordinates of the scaled objects. Methods B, |
878
|
|
|
|
|
|
|
B, and B should be used to change the scale of the display without affecting the dimensions of the objects. |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
=back |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
=head1 VIEW AREA CHANGE CALLBACK |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
I option '-changeView' can be used to specify a callback for a change of the view area. This is useful for updating a second AbstractCanvas which |
885
|
|
|
|
|
|
|
is displaying the view region of the first AbstractCanvas. |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
The callback subroutine will be passed the coordinates of the displayed box (x1, y1, x2, y2). These arguments are added after any extra arguments |
888
|
|
|
|
|
|
|
specifed by the user calling 'configure'. Example: |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
$acnv->configure(-changeView => [\&changeView, $acn2]); |
891
|
|
|
|
|
|
|
# viewAll if 2nd AbstractCanvas widget is resized. |
892
|
|
|
|
|
|
|
$acn2->CanvasBind('' => sub {$acn2->viewAll}); |
893
|
|
|
|
|
|
|
{ |
894
|
|
|
|
|
|
|
my $viewBox; |
895
|
|
|
|
|
|
|
sub changeView { |
896
|
|
|
|
|
|
|
my($canvas2, @coords) = @_; |
897
|
|
|
|
|
|
|
$canvas2->delete($viewBox) if $viewBox; |
898
|
|
|
|
|
|
|
$viewBox = $canvas2->createRectangle(@coords, -outline => 'orange'); |
899
|
|
|
|
|
|
|
} |
900
|
|
|
|
|
|
|
} |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=head1 SCROLL REGION NOTES |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
(1) The underlying I has a '-confine' option which is set to '1' by default there. With '-confine => 1' the canvas will not allow the |
905
|
|
|
|
|
|
|
display to go outside of the scroll region. This causes some methods not to work accurately, for example, the 'center' method will not be able to |
906
|
|
|
|
|
|
|
center on coordinates near to the edge of the scroll region and 'zoom out' near the edge will zoom out and pan towards the center. |
907
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
I sets '-confine => 0' by default to avoid these problems. You can change it back with: |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
$acnv->configure(-confine => 1); |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
(2) '-scrollregion' is maintained by I to include all objects on the canvas. '-scrollregion' will be adjusted automatically as objects are |
913
|
|
|
|
|
|
|
added, deleted, scaled, moved, etc.. (You can create a static scrollregion by adding a border rectangle to the canvas.) |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
(3) The bounding box of all objects is required to set the scroll region. Calculating this bounding box is expensive if the canvas has a large |
916
|
|
|
|
|
|
|
number of objects. So for performance reasons these operations will not immediately change the bounding box if they potentially shrink it: |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
coords |
919
|
|
|
|
|
|
|
delete |
920
|
|
|
|
|
|
|
move |
921
|
|
|
|
|
|
|
scale |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
Instead they will mark the bounding box as invalid, and it will be updated at the next zoom or pan operation. The only downside to this is that the |
924
|
|
|
|
|
|
|
scrollbars will be incorrect until the update. |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
If these operations increase the size of the box, changing the box is trivial and the update is immediate. |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
=head1 ROTATION LIMITATIONS |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
As it stands, the module can only rotate the following object types about their centers: |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
=over 2 |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=item * Lines |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
=item * Polygons |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
=item * Rectangles (if rectToPoly(1) is called) |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
=item * Ovals (if ovalToPoly(1) is called) |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
=back |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
All other object types (bitmap, image, arc, text, and window) can only be rotated about another point. A warning is issued if the user tries to |
945
|
|
|
|
|
|
|
rotate one of these object types about their center. Hopefully, more types will be able to center-rotate in the future. |
946
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
=head1 ROTATION DETAILS |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
To be able to rotate rectangles and ovals, this module is capable of intercepting any calls to B, B, and |
950
|
|
|
|
|
|
|
B to change them to polygons. The user should not be alarmed if B returns I when a I or I |
951
|
|
|
|
|
|
|
was created. Additionally, if you call B on a polygonized object, expect to have to manipulate all the additionally generated |
952
|
|
|
|
|
|
|
coordinates. |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
=head1 TODO |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
=over 2 |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=item - add Math::Geometry::Planar and others to polygonize, find_CM, test intersections, etc. |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
=item - maybe include Graph::Easy and some file serialization like DiagEMT XML but maybe more like a .Hrc format for the future |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
=item - maybe make Diag objects which can scale fonts or state-box sizes separately from uniform AC zooms, have EMT side scroll panel, input or output |
963
|
|
|
|
|
|
|
arrow ordering, state-box flags region and menu frameworks, overview with rubberBand of main view region, jointed arrows, arcs, splines, and pal8s |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=item - abstract rotations fully away like zoom |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
=back |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
=head1 CHANGES |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
Revision history for Perl extension Tk::AbstractCanvas: |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
=over 2 |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
=item - 1.8 H79M9Rot Sun Jul 9 09:27:50:55 -0500 2017 |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
* added test skipping in 02acnv.t to resolve L. (Thanks again Slaven!) |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
* removed broken Build.PL to resolve L. (Thank you, Slaven.) |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
=item - 1.6 H78MEDIC Sat Jul 8 14:13:18:12 -0500 2017 |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
* cleaned up examples and tested that grid works from L. (Thanks Josef!) |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
* replaced POD ampersands with 'and' and turned double-spaces after periods to single |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
=item - 1.4.A7QFZHF Mon Jul 26 15:35:17:15 2010 |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
* updated license to GPLv3 |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
=item - 1.2.75L75Nr Mon May 21 07:05:23:53 2007 |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
* added ex/* examples and tidied everything up |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
* added Ctrl rot callbacks (mocb, rlcb) and limited Motion and Release to just Ctrl + one MouseButton events |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
=item - 1.0.56BHMOt Sat Jun 11 17:22:24:55 2005 |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
* original version |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
=back |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
=head1 INSTALL |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
Please run: |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
`perl -MCPAN -e "install Tk::AbstractCanvas"` |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
or uncompress the package and run the standard: |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
`perl Makefile.PL; make; make test; make install` |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
=head1 BUGS |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
Please report any bugs or feature requests to bug-Tk-AbstractCanvas at RT.CPAN.Org, or through the web interface at |
1016
|
|
|
|
|
|
|
L. I will be notified, and then you can be updated of progress on your bug |
1017
|
|
|
|
|
|
|
as I address fixes. |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
=head1 SUPPORT |
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command (after it is installed). |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
`perldoc Tk::AbstractCanvas` |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
You can also look for information at: |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
RT: CPAN's Request Tracker |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
HTTPS://RT.CPAN.Org/NoAuth/Bugs.html?Dist=Tk-AbstractCanvas |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
AnnoCPAN: Annotated CPAN documentation |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
HTTP://AnnoCPAN.Org/dist/Tk-AbstractCanvas |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
CPAN Ratings |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
HTTPS://CPANRatings.Perl.Org/d/Tk-AbstractCanvas |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
Search CPAN |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
HTTP://Search.CPAN.Org/dist/Tk-AbstractCanvas |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
=head1 LICENSE |
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
Most source code should be Free! Code I have lawful authority over is and shall be! |
1046
|
|
|
|
|
|
|
Copyright: (c) 2005-2017, Pip Stuart. |
1047
|
|
|
|
|
|
|
Copyleft : This software is licensed under the GNU General Public License |
1048
|
|
|
|
|
|
|
(version 3 or later). Please consult L |
1049
|
|
|
|
|
|
|
for important information about your freedom. This is Free Software: you |
1050
|
|
|
|
|
|
|
are free to change and redistribute it. There is NO WARRANTY, to the |
1051
|
|
|
|
|
|
|
extent permitted by law. See L for further information. |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
=head1 AUTHORS |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
Pip Stuart (I) |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
AbstractCanvas is derived from code by: |
1058
|
|
|
|
|
|
|
Joseph Skrovan (I) |
1059
|
|
|
|
|
|
|
Rudy Albachten (I) |
1060
|
|
|
|
|
|
|
Ala Qumsieh (I) |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
=cut |