diff --git a/META6.json b/META6.json index 6665a79..bb19ed0 100644 --- a/META6.json +++ b/META6.json @@ -1,13 +1,21 @@ { - "perl" : "6.*", - "name" : "Math::Vector", - "version" : "0.5.1", - "description" : "Vector math class", - "provides" : { - "Math::Vector": "lib/Math/Vector.pm" - }, - "depends" : [], - "tags" : [ "Math" ], - "source-type" : "git", - "source-url" : "git://github.com/colomon/Math-Vector.git" -} + "name": "Math::Vector", + "description": "Vector math class", + "version": "0.6.1", + "perl": "6.d", + "authors": [ + "librasteve" + ], + "auth": "zef:librasteve", + "depends": [], + "provides": { + "Math::Vector": "lib/Math/Vector.rakumod" + }, + "license": "Artistic-2.0", + "tags": [ + "Math", + "Vector" + ], + "source-url": "git://github.com/librasteve/Math-Vector.git", + "source-type": "git" +} \ No newline at end of file diff --git a/README b/README deleted file mode 100644 index d6e2bbc..0000000 --- a/README +++ /dev/null @@ -1,12 +0,0 @@ -This is a simple attempt to create a working Vector class. Initially intended -to support 3D vectors, it turned out to be easier to support vectors of -any dimension. - -Math::Vector requires Rakudo from 7/25/2010 or later. - -Build sequence: - - ufo - make - make test - diff --git a/README.md b/README.md new file mode 100644 index 0000000..1230014 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +[![License: Artistic-2.0](https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg)](https://opensource.org/licenses/Artistic-2.0) + +# Math::Vector + +This is a fork of https://github.com/colomon/Math-Vector from v0.6.0 + +## SYNOPSIS + +```raku +#!/usr/bin/env raku +use Math::Vector; + +my $v1 = Math::Vector.new(1, 2, 3); +my $v2 = Math::Vector.new(3, 4, 0); + +say "My vector $v1 has {$v1.dim} dimensions"; #^(1, 2, 3) + +say ~ ($v1 + $v2); #^(4, 6, 3) +say ~ ($v1 * 2); #^(2, 4, 6) +say ~ ($v1 ⋅ $v2); #11 +say ~ ($v1 × $v2); #^(-12, 9, -2) +say ~ (⎡$v1⎤); #3.7416573867739413 +say ~ (-$v1); #^(-1, -2, -3) +say ~ ($v1 cmp $v2); #Less +``` + diff --git a/bin/synopsis-mv.raku b/bin/synopsis-mv.raku new file mode 100644 index 0000000..4c486c6 --- /dev/null +++ b/bin/synopsis-mv.raku @@ -0,0 +1,17 @@ +#!/usr/bin/env raku +use Math::Vector; + +my $v1 = Math::Vector.new(1, 2, 3); +my $v2 = Math::Vector.new(3, 4, 0); + +say "My vector $v1 has {$v1.dim} dimensions"; #^(1, 2, 3) + +say ~ ($v1 + $v2); #^(4, 6, 3) +say ~ ($v1 * 2); #^(2, 4, 6) +say ~ ($v1 ⋅ $v2); #11 +say ~ ($v1 × $v2); #^(-12, 9, -2) +say ~ (⎡$v1⎤); #3.7416573867739413 +say ~ (-$v1); #^(-1, -2, -3) +say ~ ($v1 cmp $v2); #Less + + diff --git a/deps.proto b/deps.proto deleted file mode 100644 index 61bfa8c..0000000 --- a/deps.proto +++ /dev/null @@ -1 +0,0 @@ -# Math::Vector has no dependencies at this time. \ No newline at end of file diff --git a/lib/Math/Vector.pm b/lib/Math/Vector.pm deleted file mode 100644 index 812628f..0000000 --- a/lib/Math/Vector.pm +++ /dev/null @@ -1,165 +0,0 @@ -use v6; -no precompilation; # avoid https://github.com/rakudo/rakudo/issues/1219 - -use Test; # so we can define is-approx-vector - -class Math::Vector does Positional -{ - has @.coordinates handles ; - - multi method new (*@x) - { - self.bless(coordinates => @x); - } - - multi method new (@x) - { - self.bless(coordinates => @x); - } - - multi method Str() - { - "(" ~ @.coordinates.join(', ') ~ ")"; - } - - multi method perl() - { - "Math::Vector.new(" ~ @.coordinates.map({.perl}).join(', ') ~ ")"; - } - - multi method Num() - { - die "Cannot call Num on Vector!"; - } - - method Dim() is DEPRECATED('dim') { - self.dim; - } - - method dim() { - @.coordinates.elems; - } - - multi sub infix:<⋅>(Math::Vector $a, Math::Vector $b where { $a.dim == $b.dim }) is export # is tighter(&infix:<+>) (NYI) - { - [+]($a.coordinates »*« $b.coordinates); - } - - multi sub infix:(Math::Vector $a, Math::Vector $b) is export - { - $a ⋅ $b; - } - - method Length() is DEPRECATED('length') { - self.length; - } - - method length() { - sqrt(self ⋅ self.conj); - } - - multi method abs() - { - self.length; - } - - multi method conj() - { - Math::Vector.new(@.coordinates>>.conj); - } - - method Unitize() is DEPRECATED('unitize') { - self.unitize; - } - - method unitize() { - my $length = self.length; - if $length > 1e-10 - { - return Math::Vector.new(@.coordinates >>/>> $length); - } - else - { - return Math::Vector.new(@.coordinates); - } - } - - multi sub infix:<+> (Math::Vector $a, Math::Vector $b where { $a.dim == $b.dim }) is export - { - Math::Vector.new($a.coordinates »+« $b.coordinates); - } - - multi sub infix:<->(Math::Vector $a, Math::Vector $b where { $a.dim == $b.dim }) is export - { - Math::Vector.new($a.coordinates »-« $b.coordinates); - } - - multi sub prefix:<->(Math::Vector $a) is export - { - Math::Vector.new(0 <<-<< $a.coordinates); - } - - multi sub infix:<*>(Math::Vector $a, $b) is export - { - Math::Vector.new($a.coordinates >>*>> $b); - } - - multi sub infix:<*>($a, Math::Vector $b) is export - { - Math::Vector.new($a <<*<< $b.coordinates); - } - - multi sub infix:(Math::Vector $a, $b) is export - { - Math::Vector.new($a.coordinates >>/>> $b); - } - - multi sub infix:<×>(Math::Vector $a where { $a.dim == 3 }, Math::Vector $b where { $b.dim == 3 }) is export - { - Math::Vector.new($a[1] * $b[2] - $a[2] * $b[1], - $a[2] * $b[0] - $a[0] * $b[2], - $a[0] * $b[1] - $a[1] * $b[0]); - } - - multi sub infix:<×>(Math::Vector $a where { $a.dim == 7 }, Math::Vector $b where { $b.dim == 7 }) is export - { - Math::Vector.new($a[1] * $b[3] - $a[3] * $b[1] - + $a[2] * $b[6] - $a[6] * $b[2] - + $a[4] * $b[5] - $a[5] * $b[4], - $a[2] * $b[4] - $a[4] * $b[2] - + $a[3] * $b[0] - $a[0] * $b[3] - + $a[5] * $b[6] - $a[6] * $b[5], - $a[3] * $b[5] - $a[5] * $b[3] - + $a[4] * $b[1] - $a[1] * $b[4] - + $a[6] * $b[0] - $a[0] * $b[6], - $a[4] * $b[6] - $a[6] * $b[4] - + $a[5] * $b[2] - $a[2] * $b[5] - + $a[0] * $b[1] - $a[1] * $b[0], - $a[5] * $b[0] - $a[0] * $b[5] - + $a[6] * $b[3] - $a[3] * $b[6] - + $a[1] * $b[2] - $a[2] * $b[1], - $a[6] * $b[1] - $a[1] * $b[6] - + $a[0] * $b[4] - $a[4] * $b[0] - + $a[2] * $b[3] - $a[3] * $b[2], - $a[0] * $b[2] - $a[2] * $b[0] - + $a[1] * $b[5] - $a[5] * $b[1] - + $a[3] * $b[4] - $a[4] * $b[3]); - } - - multi sub infix:(Math::Vector $a, Math::Vector $b) is export - { - $a × $b; - } - - multi sub circumfix:<⎡ ⎤>(Math::Vector $a) is export - { - $a.length; - } - - sub is-approx-vector(Math::Vector $a, Math::Vector $b, $desc) is export - { - ok(($a - $b).length < 0.00001, $desc); - } -} - -subset Math::UnitVector of Math::Vector where { (1 - 1e-10) < $^v.length < (1 + 1e-10) }; diff --git a/lib/Math/Vector.rakumod b/lib/Math/Vector.rakumod new file mode 100644 index 0000000..e8d63c4 --- /dev/null +++ b/lib/Math/Vector.rakumod @@ -0,0 +1,182 @@ +use v6.d; + +class Math::Vector does Positional +{ + has @.components handles ; + + multi method new (*@x) + { + self.bless(components => @x); + } + + multi method new (@x) + { + self.bless(components => @x); + } + + method Str() + { + "^(" ~ @.components.join(', ') ~ ")"; + } + + method raku() + { + "Math::Vector.new(" ~ @.components.map({.perl}).join(', ') ~ ")"; + } + + method Num() + { + die "Cannot call Num on Vector!"; + } + + method dim() + { + @.components.elems; + } + + method add($other) + { + Math::Vector.new(self.components »+« $other.components); + } + + method subtract($other) + { + Math::Vector.new(self.components »-« $other.components); + } + + method negate() + { + Math::Vector.new(0 <<-<< self.components); + } + + method scale($other) + { + Math::Vector.new(self.components >>*>> $other); + } + + method dot($other) + { + [+](self.components »*« $other.components); + } + + method length() { + sqrt(self.dot: self.conj); + } + + method abs() + { + self.length; + } + + method cross($other) + { + my ($a, $b) := self, $other; + + Math::Vector.new( + $a[1] * $b[2] - $a[2] * $b[1], + $a[2] * $b[0] - $a[0] * $b[2], + $a[0] * $b[1] - $a[1] * $b[0], + ); + } + + method unitize() + { + my $length = self.length; + if $length > 1e-10 + { + return Math::Vector.new(@.components >>/>> $length); + } + else + { + return Math::Vector.new(@.components); + } + } + + method conj() + { + Math::Vector.new(@.components>>.conj); + } + + method round($r) + { + Math::Vector.new(@.components>>.round($r)); + } + + method cmp($other) + { + my $tol := $*TOLERANCE; + + given self.length - $other.length + { + when * > $tol { More } + when -$tol < * < $tol { Same } + when -$tol > * { Less } + } + + } +} + + +subset Math::UnitVector of Math::Vector where { (1 - 1e-10) < $^v.length < (1 + 1e-10) }; + + +multi infix:<+> (Math::Vector $a, Math::Vector $b where { $a.dim == $b.dim }) is export +{ + $a.add: $b; +} + +multi infix:<->(Math::Vector $a, Math::Vector $b where { $a.dim == $b.dim }) is export +{ + $a.subtract: $b; +} + +multi prefix:<->(Math::Vector $a) is export +{ + $a.negate; +} + +multi infix:<*>(Math::Vector $a, $b) is export +{ + $a.scale: $b; +} + +multi infix:<*>($a, Math::Vector $b) is export +{ + $b.scale: $a; +} + +multi infix:(Math::Vector $a, $b) is export +{ + $a.scale: 1 / $b; +} + +multi infix:<⋅>(Math::Vector $a, Math::Vector $b where { $a.dim == $b.dim }) is export +{ + $a.dot: $b; +} + +multi infix:(Math::Vector $a, Math::Vector $b where { $a.dim == $b.dim }) is export +{ + $a.dot: $b; +} + +multi infix:<×>(Math::Vector $a where { $a.dim == 3 }, Math::Vector $b where { $b.dim == 3 }) is export +{ + $a.cross: $b; +} + +multi infix:(Math::Vector $a where { $a.dim == 3 }, Math::Vector $b where { $b.dim == 3 }) is export +{ + $a.cross: $b; +} + +multi circumfix:<⎡ ⎤>(Math::Vector $a) is export +{ + $a.length; +} + +multi infix:(Math::Vector $a, Math::Vector $b where { $a.dim == $b.dim }) is export +{ + $a.cmp: $b; +} + diff --git a/t/01-basics.t b/t/01-basics.rakutest similarity index 72% rename from t/01-basics.t rename to t/01-basics.rakutest index 6762bc5..fc59489 100644 --- a/t/01-basics.t +++ b/t/01-basics.rakutest @@ -1,7 +1,12 @@ -use v6; +use v6.d; use Math::Vector; use Test; +sub is-approx-vector(Math::Vector $a, Math::Vector $b, $desc) +{ + ok( ($a cmp $b) ~~ Same, $desc); +} + my $v1 = Math::Vector.new(1, 2, 3); my Math::Vector $v2 = Math::Vector.new(3, 4, 0); my @v3 = (-1, 0, 2); @@ -26,18 +31,18 @@ isa-ok($v7, Math::Vector, "Variable is of type Math::Vector"); isa-ok($vcrazy, Math::Vector, "Variable is of type Math::Vector"); does-ok($v1, Positional, "Vector is Positional"); -is(~$v1, "(1, 2, 3)", "Stringify works"); -is(~$v3, "(-1, 0, 2)", "Stringify works"); -is(~$origin3d, "(0, 0, 0)", "Stringify works"); -is(~$v5, "(1, 2, 3, 4, 5)", "Stringify works"); -is(~$vcrazy, "((1, 2, 3), (-1, 0, -1))", "Stringify works"); +is(~$v1, "^(1, 2, 3)", "Stringify works"); +is(~$v3, "^(-1, 0, 2)", "Stringify works"); +is(~$origin3d, "^(0, 0, 0)", "Stringify works"); +is(~$v5, "^(1, 2, 3, 4, 5)", "Stringify works"); +is(~$vcrazy, "^(^(1, 2, 3), ^(-1, 0, -1))", "Stringify works"); is($v1[0], 1, "[] works on Vector"); is($v1[1..2], (2, 3), "[] works on Vector"); -is(~EVAL($v1.perl), ~$v1, ".perl works"); -is(~EVAL($v9.perl), ~$v9, ".perl works"); -is(~EVAL($vcrazy.perl), ~$vcrazy, ".perl works"); +is(~EVAL($v1.raku), ~$v1, ".raku works"); +is(~EVAL($v9.raku), ~$v9, ".raku works"); +is(~EVAL($vcrazy.raku), ~$vcrazy, ".raku works"); is $v11.conj, Math::Vector.new(0-i,1-i), ".conj works"; @@ -48,29 +53,17 @@ is($v7.dim, 7, "dim works for 7D Math::Vector"); is-approx($v7 ⋅ $v8, 0, "Perpendicular Math::Vectors have 0 dot product"); #basic math tests -is(~($v1 + $v2), "(4, 6, 3)", "Basic sum works"); -is(~($v7 + $v9), "(2, 2, 3, 4, 5, 6, 7)", "Basic sum works, 7D"); +is(~($v1 + $v2), "^(4, 6, 3)", "Basic sum works"); +is(~($v7 + $v9), "^(2, 2, 3, 4, 5, 6, 7)", "Basic sum works, 7D"); is($v1 + $v2, $v2 + $v1, "Addition is commutative"); is(($v1 + $v2) + $v3, $v1 + ($v2 + $v3), "Addition is associative"); is($v1 + $origin3d, $v1, "Addition with origin leaves original"); -# { -# my Math::Vector $a = $v1; -# $a += $v2; -# is(~($v1 + $v2), ~$a, "+= works"); -# } -# is(~($v1 + $v2), "(4, 6, 3)", "Basic sum works"); - -is(~($v1 - $v2), "(-2, -2, 3)", "Basic subtraction works"); +is(~($v1 - $v2), "^(-2, -2, 3)", "Basic subtraction works"); is($v1 - $v2, -($v2 - $v1), "Subtraction is anticommutative"); is($v1 - $origin3d, $v1, "Subtracting the origin leaves original"); is(-$origin3d, $origin3d, "Negating the origin leaves the origin"); -is(~(-$v2), "(-3, -4, 0)", "Negating works"); -# { -# my Math::Vector $a = $v1; -# $a -= $v2; -# is(~($v1 - $v2), ~$a, "+= works"); -# } +is(~(-$v2), "^(-3, -4, 0)", "Negating works"); #lengths is($origin3d.length, 0, "Origin has 0 length"); @@ -130,7 +123,7 @@ dies-ok( { $v7 dot $v5 }, "You can't do dot products of different dimensions"); } #cross product tests -is(~($v1 × $v2), "(-12, 9, -2)", "Basic cross product works"); +is(~($v1 × $v2), "^(-12, 9, -2)", "Basic cross product works"); for flat ($v1, $v2, $v3) X ($v1, $v2, $v3) -> $x, $y { @@ -142,17 +135,7 @@ for flat ($v1, $v2, $v3) X ($v1, $v2, $v3) -> $x, $y "|x × y|^2 = |x|^2 * |y|^2 - (x ⋅ y)^2"); } -for flat ($v7, $v8, $v9, $v10) X ($v7, $v8, $v9, $v10) -> $x, $y -{ - my $cross = $x × $y; - is-approx($cross ⋅ $x, 0, "(x × y) ⋅ x = 0"); - is-approx($cross ⋅ $y, 0, "(x × y) ⋅ y = 0"); - is-approx-vector($cross, -($y × $x), "x × y = -y × x"); - is-approx($cross.length ** 2, $x.length ** 2 * $y.length ** 2 - ($x ⋅ $y) ** 2, - "|x × y|^2 = |x|^2 * |y|^2 - (x ⋅ y)^2"); -} - -lives-ok { $v7 cross $v8, "7D cross product works writing out cross"} +dies-ok { $v7 cross $v8, "7D cross product works writing out cross"} dies-ok( { $v1 × $v7 }, "You can't do cross products of different dimensions"); dies-ok( { $v5 × $v6 }, "You can't do 5D cross products"); dies-ok( { $v1 cross $v7 }, "You can't do cross products of different dimensions"); @@ -186,30 +169,4 @@ dies-ok( { $v5 cross $v6 }, "You can't do 5D cross products"); # isa-ok(+$v1, Math::Vector, "Prefix + works on the Math::Vector class"); dies-ok( { $v1.Num; }, "Make sure .Num does not work on 3D Math::Vector"); -# test extensions -# class Math::VectorWithLength is Math::Vector -# { -# has $.length; -# -# multi method new (*@x) -# { -# self.bless(*, coordinates => @x, length => sqrt [+] (@x »*« @x)); -# } -# -# multi method new (@x) -# { -# self.bless(*, coordinates => @x, length => sqrt [+] (@x »*« @x)); -# } -# -# submethod Length -# { -# $.length; -# } -# } -# -# my Math::VectorWithLength $vl = Math::VectorWithLength.new($v7.coordinates); -# isa-ok($vl, Math::VectorWithLength, "Variable is of type Math::VectorWithLength"); -# my $vlc = EVAL($vl.perl); -# isa-ok($vlc, Math::VectorWithLength, "EVAL'd perl'd variable is of type Math::VectorWithLength"); - -done-testing; \ No newline at end of file +done-testing;