holli has asked for the wisdom of the Perl Monks concerning the following question:
use v6; use Math::Angle; subset DefinedAngle of Math::Angle where *.defined; subset UndefinedAngle of Math::Angle where !*.defined; subset DefinedCool of Cool where *.defined; subset UndefinedCool of Cool where !*.defined; unit class Math::Triangle; has Cool $.a; has Cool $.b; has Cool $.c; has Math::Angle $.alpha; has Math::Angle $.beta; has Math::Angle $.gamma; my @all_angles = ('alpha', 'beta', 'gamma'); my @all_sides = ('a', 'b', 'c'); sub prefix:<△> (*@things) is looser(&[,]) returns Math::Triangle + is export( :operators ) { my %things = @things; return Math::Triangle.new: |%things; }; sub triangle ( Cool :$a, Cool :$b, Cool :$c, Math::Angle :α(:$alp +ha), Math::Angle :β(:$beta), Math::Angle :γ(:$gamma) ) is e +xport( :operators ) { return Math::Triangle.new( a => $a, b => $b, c => $c, alpha => $alph +a, beta => $beta, gamma => $gamma ); } method Str() { return "△ α<$!alpha>, β<$!beta>,γ<$!gamma>, a +<{$!a||''}>, b<{$!b||''}>, c<{$!c||''}>"; } method BUILD( *%_ [ Cool :$a, Cool :$b, Cool :$c, Math::Angle :α( +:$alpha), Math::Angle :β(:$beta), Math::Angle :γ(:$gamma) ] + ) { $!a = $a; $!alpha = $alpha; $!b = $b; $!beta = $beta; $!c = $c; $!gamma = $gamma; self.check(); self.compute(); } method angles() { my %angles = ( alpha => $!alpha, beta => $!beta, gamma => $!gamma ); @all_angles.grep( { %angles{$_}:exists && %angles{$_}.defined } ); } method sides () { my %sides = ( a => $!a, b => $!b, c => $!c ); @all_sides.grep( { %sides{$_}:exists && %sides{$_}.defined } ); } method check() { my @angles = self.angles; my @sides = self.sides; die "Sides do not satisfy the triangle inequality" if @sides == 3 && !satifies_triangle_inequality( $!a, $!b, $!c ); if ( @angles > 0 ) { my $angle_sum = 0; for ($!alpha, $!beta, $!gamma) -> $angle { next unless $angle.defined; die "Invalid angle ($angle). An angle in a triangle must be grea +ter than zero and smaller than 180°" unless 0 < $angle.degrees < 180; $angle_sum += $angle.degrees; } die "The sum of two angles must be smaller than 180°" if @angles == 2 && $angle_sum >= 180; die "The sum of three angles must be 180°" if @angles == 3 && $angle_sum != 180; die "Triangle with three known sides only is not determined" if @angles == 3 && @sides == 0; } } multi method compute() { repeat { say "intermediate ", self.Str; } until self.compute( sides => self.sides.Array, angles => self.angl +es.Array ); say "final ", self.Str; } multi method compute( :@sides where *.elems == 3, :@angles where *.ele +ms == 3 ) { return True; } multi method compute( :@sides where *.elems == 3, :@angles where *.ele +ms == 0 ) { #say "compute sides<3>, angles<0>"; $!alpha = cosine_law_a( $!a, $!b, $!c ); $!beta = cosine_law_a( $!b, $!a, $!c ); $!gamma = cosine_law_a( $!c, $!a, $!b ); return True; } multi method compute( :@sides where *.elems == 2, :@angles where *.ele +ms == 3 ) { #say "compute sides<2>, angles<3>"; $!a = sine_law($!alpha, $!beta, $!b ) unless $!a.defined; $!b = sine_law($!beta, $!alpha, $!a ) unless $!b.defined; $!c = sine_law($!gamma, $!alpha, $!a ) unless $!c.defined; return False; #say self.Str; } multi method compute( :@sides where *.elems == 2, :@angles where *.ele +ms == 1 ) { say "compute sides<2>, angles<1>"; # SAS-case $!a = cosine_law($!alpha, $!b, $!c).round(0.000001) if !$!a.defined && ( $!alpha.defined && $!b.defined && $!c.defined + ); $!b = cosine_law($!beta, $!a, $!c).round(0.000001) if !$!b.defined && ( $!beta.defined && $!a.defined && $!c.defined +); $!c = cosine_law($!gamma, $!a, $!b).round(0.000001) if !$!c.defined && ( $!gamma.defined && $!a.defined && $!b.defined + ); # SSA if $!alpha.defined && $!a.defined { ($!beta, $!gamma) = self.solve_SsA( $!alpha, $!a, $!b, $!c ) } elsif $!beta.defined && $!b.defined { ($!alpha, $!gamma) = self.solve_SsA( $!beta, $!b, $!a, $!c ) } elsif $!gamma.defined && $!c.defined { ($!alpha, $!beta) = self.solve_SsA( $!gamma, $!c, $!a, $!b ) } return False; } # WWS- and WSW- cases multi method compute( :@sides where *.elems == 1, :@angles where *.ele +ms == 2 ) { say "compute sides<1>, angles<2>"; # calculate the missing angle $!alpha = self.angle_sum_law( $!beta, $!gamma ) unless $!alpha.defined; $!beta = self.angle_sum_law( $!alpha, $!gamma ) unless $!beta.defined; $!gamma = self.angle_sum_law( $!alpha, $!beta ) unless $!gamma.defined; #the missing sides $!a = $!b.defined ?? self.sine_law($!alpha, $!beta, $!b ) !! $!c.defined ?? self.sine_law($!alpha, $!gamma, $!c ) !! 0 unless $!a.defined; $!b = $!a.defined ?? self.sine_law($!beta, $!alpha, $!a ) !! $!c.defined ?? self.sine_law($!beta, $!gamma, $!c ) !! 0 unless $!b.defined; $!c = $!a.defined ?? self.sine_law($!gamma, $!alpha, $!a ) !! $!b.defined ?? self.sine_law($!gamma, $!beta, $!b ) !! 0 unless $!b.defined; return False; } # The SSA- or ASS-case is only unique, if the known angle is is opposi +te # to the bigger one of the known sides ( SsA case) method solve_SsA( Math::Angle $angle, Cool $opposite_side_of_angle, Co +ol $other_side1, Cool $other_side2 ) { #say "solve_SsA"; return self.solve_ssw( $angle, $opposite_side_of_angle, $other_sid +e1 ) if $other_side1.defined && $opposite_side_of_angle >= $other_sid +e1; return self.solve_ssw( $angle, $opposite_side_of_angle, $other_sid +e2 ) if $other_side2.defined && $opposite_side_of_angle >= $other_sid +e2; } #alpha, a, b : where a > b method solve_ssw( Math::Angle $angle, Cool $opposite_side_of_angle, Co +ol $other_side ) { #say "solve_ssw $angle, $opposite_side_of_angle, $other_side"; return gather { take $_ = sine_law_a( $other_side, $angle, $opposite_side_of_ang +le ); take angle_sum_law( $angle, $_ ); }; } # isoceles: all sides of equal length method is_isoceles() { return $!a == $!b == $!c; } # scalene: two sides have the same length method is_scalene() { return $!a == $!b || $!a == $!c || $!c == $!b; } # oblique: none of the angles is a right method is_oblique() { my $right = Math::Angle.new( degrees => 90); return $!alpha != $right && $!beta != $right && $!gamma != $right; } # right: one of the angles is 90° method is_right() { my $right = Math::Angle.new( degrees => 90); return $!alpha == $right || $!beta == $right || $!gamma == $right; } #acute: all angles < 90° method is_acute() { my $right = Math::Angle.new( degrees => 90); return $!alpha < $right && $!beta < $right && $!gamma < $right; } #obtuse: one angle > 90° method is_obtuse() { my $right = Math::Angle.new( degrees => 90); return $!alpha > $right || $!beta > $right || $!gamma > $right; } method is_similar_to( Math::Triangle ) { } sub cosine_law( Math::Angle $angle, Cool $cathede1, Cool $cathede2 ) { return sqrt( $cathede1**2 + $cathede2**2 - ( 2 * $cathede1 * $cathed +e2 * cos($angle.radians) ) ); } sub cosine_law_a( Cool $opposite_side_of_angle, Cool $other_side1, Coo +l $other_side2 ) { return Math::Angle.new( radians => acos( ( $other_side1**2 + $other_side2**2 - $opposite_side_of_angle**2 + ) / (2 * $other_side1 * $other_side2) )); } sub sine_law( Math::Angle $angle, Math::Angle $known_angle, Cool $know +n_side ) { # $side / sin($angle) = $known_side / sin( $known_angle ) return ( sin($angle.radians) * $known_side ) / sin( $known_angle.ra +dians ) ; } sub sine_law_a( Cool $side, Math::Angle $known_angle, Cool $known_side + ) { # $side / sin($angle) = $known_side / sin( $known_angle ) # $side = ( $known_side * sin( $angle ) ) / sin( $known_side ) # $side * sin( $known_side ) = $known_side * sin( $angle ) # sin( $angle ) = $side * sin( $known_side ) / $known_side return Math::Angle.new( radians => asin( $side * sin( $known_angle.r +adians ) / $known_side ) ) ; } sub angle_sum_law(DefinedAngle $a, DefinedAngle $b) { return Math::Angle.new( radians => pi - $a.radians - $b.radians ); } sub satifies_triangle_inequality($a, $b, $c) { return $a + $b > $c && $c + $a > $b && $b + $c > $a; }
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: RFC: Math::Triangle (Perl 6)
by Laurent_R (Canon) on Sep 18, 2017 at 05:32 UTC | |
|
Re: RFC: Math::Triangle (Perl 6)
by salva (Canon) on Sep 18, 2017 at 06:52 UTC | |
by holli (Abbot) on Sep 18, 2017 at 12:44 UTC | |
by Laurent_R (Canon) on Sep 18, 2017 at 22:08 UTC | |
by salva (Canon) on Sep 18, 2017 at 13:08 UTC | |
by holli (Abbot) on Sep 18, 2017 at 13:16 UTC | |
by salva (Canon) on Sep 18, 2017 at 14:48 UTC | |
| |
|
Re: RFC: Math::Triangle (Perl 6)
by Arunbear (Prior) on Sep 19, 2017 at 11:00 UTC |