package Math::Path; # 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; our $recursive_depth = 2; sub new { my ($class, @coords) = @_; my $self = {}; bless $self, $class; $self -> {content} = (); my @path = (); my $tmp = []; for (my $i = 0; $i < @coords; $i ++) { my @arr = @{$coords [$i]}; if (@arr == 3) { push (@{$tmp}, \@arr); } else { push (@{$tmp}, \@arr); push (@path, $tmp); $tmp = []; } } $self -> {curves} = \@path; $self -> {lastPoint} = undef; $self -> {lenght} = 0; $self -> update (); return $self; } sub drawLine { my ($self, $x0, $y0, $x1, $y1) = @_; if (!defined $self -> {content} || @{$self -> {content}} == 0) { push (@{$self -> {content}}, [$x0, $y0]); } push (@{$self -> {content}}, [$x1, $y1]); } sub _calcul_lenght { my ($self) = @_; my @content = @{$self -> {content}}; my $x = $content [0] -> [0]; my $y = $content [0] -> [1]; my $lenght = 0; for (my $i = 0; $i < @content; $i ++) { my $dx = $content [$i] -> [0] - $x; my $dy = $content [$i] -> [1] - $y; my $d = sqrt (($dx) ** 2 + ($dy) ** 2); $lenght += $d; $x = $content [$i] -> [0]; $y = $content [$i] -> [1]; } $self -> {lenght} = $lenght; } sub drawBezierRecursive { my ($self, $p0, $p1, $p2, $p3, $level) = @_; if ($level <= 0) { $self -> drawLine ($p0 -> [0] + 0.5, $p0 -> [1] + 0.5, $p3 -> [0] + 0.5, $p3 -> [1] + 0.5); } else { my $left0 = [$p0 -> [0], $p0 -> [1]]; my $left1 = [($p0 -> [0] + $p1 -> [0]) / 2, ($p0 -> [1] + $p1 -> [1]) / 2]; my $left2 = [($p0 -> [0] + 2*$p1 -> [0] + $p2 -> [0]) / 4, ($p0 -> [1] + 2*$p1 -> [1] + $p2 -> [1]) / 4]; my $left3 = [($p0 -> [0] + 3*$p1 -> [0] + 3*$p2 -> [0] + $p3 -> [0]) / 8, ($p0 -> [1] + 3*$p1 -> [1] + 3*$p2 -> [1] + $p3 -> [1]) / 8]; my $right0 = [$left3 -> [0], $left3 -> [1]]; my $right1 = [($p1 -> [0] + 2*$p2 -> [0] + $p3 -> [0]) / 4, ($p1 -> [1] + 2*$p2 -> [1] + $p3 -> [1]) / 4]; my $right2 = [($p2 -> [0] + $p3 -> [0]) / 2, ($p2 -> [1] + $p3 -> [1]) / 2]; my $right3 = [$p3 -> [0],$p3 -> [1]]; $self -> drawBezierRecursive ($left0, $left1, $left2, $left3, $level -1); $self -> drawBezierRecursive ($right0, $right1, $right2, $right3, $level -1); } } sub update { my ($self) = @_; my @curr_curve = (); $self -> {content} = (); $self -> {lastPoint} = undef; my @curves = @{$self -> {curves}}; for (my $i = 0; $i < @curves; $i++) { @curr_curve = @{$curves [$i]}; if (@curr_curve == 1) { push (@{$self -> {content}}, $curr_curve [0]); $self -> {lastPoint} = $curr_curve [0]; } elsif (@curr_curve == 2) { push (@{$self -> {content}}, $curr_curve [0]); push (@{$self -> {content}}, $curr_curve [1]); $self -> {lastPoint} = $curr_curve [1]; } elsif (@curr_curve == 3) { $self -> drawBezierRecursive ($self -> {lastPoint}, $curr_curve [0], $curr_curve [1], $curr_curve [2], $recursive_depth); $self -> {lastPoint} = $curr_curve [2]; } elsif (@curr_curve == 4) { $self -> drawBezierRecursive ($curr_curve [0], $curr_curve [1], $curr_curve [2], $curr_curve [3], $recursive_depth); $self -> {lastPoint} = $curr_curve [3]; } } $self -> _calcul_lenght (); } sub getRegularPoints { my ($self, $nb) = @_; my @points = (); my $troncon = $self -> {lenght} / $nb; my @content = @{$self -> {content}}; my $current_troncon = 0; my $x = $content [0] -> [0]; my $y = $content [0] -> [1]; my $dx = 1; my $dy = 1; my $lenght = 0; for (my $i = 0; $i < @content; $i ++) { $dx = $content [$i] -> [0] - $x; $dy = $content [$i] -> [1] - $y; my $d = sqrt (($dx) ** 2 + ($dy) ** 2); $lenght += $d; $current_troncon += $d; while ($current_troncon >= $troncon) { my $dtp = $troncon - ($current_troncon - $d); my $px = $x + $dx * $dtp / $d; my $py = $y + $dy * $dtp / $d; $x = $px; $y = $py; $d -= $dtp; $dx = $content [$i] -> [0] - $x; $dy = $content [$i] -> [1] - $y; $current_troncon -= $troncon; push (@points, [$x, $y, atan2 ($dx, $dy)]); } $x = $content [$i] -> [0]; $y = $content [$i] -> [1]; } push (@points, [$x, $y, atan2 ($dx, $dy)]); return @points; }