diff options
Diffstat (limited to 'src/MTools/Widget/MArrow.pm')
-rw-r--r-- | src/MTools/Widget/MArrow.pm | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/MTools/Widget/MArrow.pm b/src/MTools/Widget/MArrow.pm new file mode 100644 index 0000000..abfb5a3 --- /dev/null +++ b/src/MTools/Widget/MArrow.pm @@ -0,0 +1,297 @@ +package MTools::Widget::MArrow; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +use strict; +use Math::Trig; +require Exporter; +use Tk::Zinc::Graphics; +use MTools; +use MTools::GUI::MGroup; + +use vars qw /@ISA/; +@ISA = qw(Exporter); +my @EXPORT = qw(&arrowCoords); + +# orientation des flèches +my $direction2angle = {'left' => 180, + 'top'=> 90, + 'right' => 0, + 'bottom' => 270, + 'w' => 180, + 'nw'=> 135, + 'n' => 90, + 'ne'=> 45, + 'e' => 0, + 'se' => 315, + 's' => 270, + 'sw' => 225 + }; + + +BEGIN +{ + @ISA = qw /MTools::GUI::MGroup/; +} + +sub new { + my ($class, %options) = @_; + my $parentgroup = $options {'parent'} ? $options {'parent'} : 1; + my $self = new MTools::GUI::MGroup ($parentgroup); + + + bless $self, $class; + + # coords + $self -> coords ($options {'coords'}) if ($options {'coords'}); + + # coordonnées de la ligne support de flèche + if ($options {'linecoords'}) { + $self -> {'linecoords'} = $options {'linecoords'}; + $self -> {'length'} = &distance(@{$self -> {'linecoords'}->[-1]}, + @{$self -> {'linecoords'}->[-2]}); + } else { + $self -> {'length'} = (defined $options {'length'}) ? $options {'length'} : 100; + $self -> {'linecoords'} = [[0,0],[$self -> {'length'},0]]; + } + + $self -> {'width'} = ($options{'width'}) ? $options{'width'} : $self -> {'length'}; + + + # paramètres de direction + if (defined $options {angle}) { + $self -> {'angle'} = $options {'angle'}; + $self -> {'direction'} = undef; + + } else { + $self -> {'direction'} = ($options {'direction'}) ? $options {'direction'} : 'right'; + } + + + # paramètres de forme + # (style, longueur et angle du marqueur flèche, épaisseur et rayon du tracé) + $self -> {'head_style'} = $options {'head_style'}; + $self -> {'head_length'} = (defined $options {'head_length'}) ? $options {'head_length'} : $self -> {'length'}/2; + + $self -> {'thick'} = ($options{'thick'}) + ? $options{'thick'} : $self -> {'length'}/5; + $self -> {'radius'} = (defined $options{'radius'}) + ? $options{'radius'} : $self -> {'thick'}/2; + $self -> {'head_angle'} = ($options{'head_angle'}) + ? $options{'head_angle'} : 45; + + # style graphique + $self -> {'style'} = $options {style}; + + + $self -> build; + + return $self; +} + + +sub build { + my $self = shift; + + # le group de transformation (rotation) de la flèche + $self -> {'r_group'} = new MTools::GUI::MGroup ($self -> {instance}); + + # calcul des coordonnées de la curve + my $coords = $self->arrowCoords; + + # style de l'item zinc + my $style = {-itemtype => 'curve', + -coords => $coords, + -params => {-closed => 1, + %{$self -> {'style'}}, + }, + }; + + # item TkZinc de forme de la flèche + $self -> {'-form'} = &buildZincItem($zinc, $self -> {'r_group'} -> {instance}, %{$style}); + + # orientation + $self->direction; + +} + + +sub arrowCoords { + my ($self, %options) = @_; + my @pts; + + my $width = ($options{'width'}) ? $options{'width'} : $self -> {'width'};; + my $head_length = ($options{'head_length'}) ? $options{'head_length'} : $self -> {'head_length'}; + my $thick = ($options{'thick'}) ? $options{'thick'} : $self -> {'thick'}; + my $head_angle = ($options{'head_angle'}) ? $options{'head_angle'} : $self -> {'head_angle'}; + my $linecoords = ($options{'linecoords'}) ? $options{'linecoords'} : $self -> {'linecoords'}; + my $radius = ($options{'radius'}) ? $options{'radius'} : $self -> {'radius'}; + + my ($pt0,$pt1) = reverse @{$linecoords}; + my $endangle = &lineAngle($pt0,$pt1); + + + # points de flèche + my ($ptA,$ptB); + + if (!defined $head_length) { + my $b_sin = sin(deg2rad($endangle + $head_angle)); + $head_length = ($b_sin) ? abs(($width/2) / $b_sin) : $width/2; + + } elsif ($head_length) { + my ($x2,$y2) = &rad_point($pt0, $head_length, $endangle -90 - $head_angle); + $ptA = [$x2,$y2]; + my ($x3,$y3) = &rad_point($pt0, $head_length, $endangle -90 + $head_angle); + $ptB = [$x3,$y3]; + } + + # dl décalage de ligne = épaisseur forme /2 + my $dl = $thick/2; + + # la liste des points de la flèche + # (si $head_length = 0 pas de marker pointe) + my @vertex = reverse @{$linecoords}; + pop @vertex; + push (@vertex, @{$linecoords}); + if ($head_length) { + push(@vertex, ($ptA,$pt0,$ptB)); + } else { + push(@vertex, $pt1); + } + + # liste (booleens) du type raccord des sommets + my @corners; + + my $numfaces = scalar(@vertex); + my $previous = $vertex[-1]; + + # on parcours la forme d'arete en arete + for (my $i = 0; $i < $numfaces; $i++) { + my $pt = $vertex[$i]; + my $next = ($i < ($numfaces -1)) ? $vertex[$i+1] : $vertex[0]; + my ($angle,$bis); + + # angle du sommet + ($angle,$bis) = &vertexAngle($previous, $pt, $next); + + # angle plat : projection du contour + if ($angle < 1) { + $angle = &lineNormal($pt,$next); + my ($x2,$y2) = &rad_point($pt, $dl, $angle-90); + my ($x1,$y1) = &rad_point($pt, $dl, $angle+90); + push (@pts, [$x1,$y1]); + push (@pts, [$x2,$y2]); + + # accumulation d'un raccord supplémentaire + push(@corners, 1); + + + } else { + + # distance au centre du cercle inscrit : rayon/sinus demi-angle + my $sin = sin(deg2rad($angle/2)); + my $delta = ($sin) ? abs($dl/$sin) : $dl; + + # calcul du point de contour (projection angulaire sur mediatrice) + my ($x,$y) = &rad_point($pt, $delta, $bis-90); + push (@pts, [$x,$y]); + + } + + # ajout d'un raccord + push(@corners, 1); + + # nouveau point précédent + $previous = $pt; + } + + + # calcul des raccords d'angle si option radius + if ($radius) { + if ($head_length) { + push @pts, shift @pts; + $corners[-1] = 0; + $corners[-7] = 0; + + if (scalar @{$linecoords} > 2) { + @corners[0] = 0; + for (my $il = 2; $il < scalar @{$linecoords}; $il++) { + $corners[$il*2] = 0; + } + } + } + my $newpts = &roundedCurveCoords(\@pts, + -radius => $radius, + -corners => \@corners); + @pts = @{$newpts}; + } + + return \@pts; +} + + +sub direction { + my $self = shift; + my $angle; + + if (defined $self -> {'angle'}) { + $angle = $self -> {'angle'}; + $self -> {'direction'} = undef; + + } else { + + if ($self -> {'direction'}) { + $angle = $direction2angle->{$self -> {'direction'}}; + } else { + $self -> {'direction'} = 'right'; + $angle = $direction2angle->{'right'}; + } + + } + + if (defined $angle) { + $self -> {'r_group'} -> treset; + $self -> {'r_group'} -> rotate (deg2rad($angle)*-1); + } + +} + + +sub distance +{ + my @p = @_; + my $d = @p / 2; # nombre de dimensions + + # 2 dimensions + return sqrt(($_[0] - $_[2])**2 + ($_[1] - $_[3])**2) + if ($d == 2); + + my $S = 0; + my @p0 = splice @p, 0, $d; # point de départ + + for (my $i = 0; $i < $d; $i++) { + my $di = $p0[$i] - $p[$i]; + $S += $di * $di; + } + + return sqrt($S); + +} + + +1; + |