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