# http://mathling.com/core/vector library module

http://mathling.com/core/vector

Vectors of numbers

Copyright© Mary Holstege 2020-2023

CC-BY (https://creativecommons.org/licenses/by/4.0/)

Status: Incomplete

### Imports

http://mathling.com/core/utilitiesimport module namespace util="http://mathling.com/core/utilities" at "../core/utilities.xqy"http://mathling.com/core/config

import module namespace config="http://mathling.com/core/config" at "../core/config.xqy"http://mathling.com/core/errors

import module namespace errors="http://mathling.com/core/errors" at "../core/errors.xqy"

### Functions

####
__Function__: as-dimension

declare function as-dimension($vector as xs:double*, $dim as xs:integer) as xs:double*

__Function__: as-dimension

declare function as-dimension($vector as xs:double*, $dim as xs:integer) as xs:double*

as-dimension()

Cast the vector to a vector with the given number of dimensions.

If we are reducing dimensions, just remove excess coordinates.

If we are expanding dimensions, fill in with 0.

##### Params

- vector as xs:double*: source vector
- dim as xs:integer: new dimension of vector

##### Returns

- xs:double*: new vector

declare function this:as-dimension($vector as xs:double*, $dim as xs:integer) as xs:double* { ($vector, for $i in 1 to $dim return 0E0)[position() <= $dim] }

####
__Function__: unquote

declare function unquote($string as xs:string) as xs:double*

__Function__: unquote

declare function unquote($string as xs:string) as xs:double*

unquote()

Parse a string representation of a vector and produce a vector.

The string consists of the coordinates in with commas and

optional spaces in between.

##### Params

- string as xs:string: the input string to parse

##### Returns

- xs:double*: vector

declare function this:unquote($string as xs:string) as xs:double* { tokenize($string, "[ ]*,[ ]*")!xs:double(.) }

####
__Function__: dimension

declare function dimension($vector as xs:double*) as xs:integer

__Function__: dimension

declare function dimension($vector as xs:double*) as xs:integer

dimension()

Return the dimension of the vector.

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:integer: length of vector

declare function this:dimension($vector as xs:double*) as xs:integer { count($vector) }

####
__Function__: map

declare function map($f as function(xs:double) as xs:double,
$vector as xs:double*) as xs:double*

__Function__: map

declare function map($f as function(xs:double) as xs:double, $vector as xs:double*) as xs:double*

map()

Map a function over the coordinates of a vector, returning the new vector.

##### Params

- f as function(xs:double)asxs:double: function from coordinate to coordinate
- vector as xs:double*: input vector

##### Returns

- xs:double*: application of function to each coordinate

declare function this:map( $f as function(xs:double) as xs:double, $vector as xs:double* ) as xs:double* { for-each($vector, $f) }

####
__Function__: map

declare function map($f as function(xs:double) as xs:double,
$vector as xs:double*,
$d as xs:integer) as xs:double*

__Function__: map

declare function map($f as function(xs:double) as xs:double, $vector as xs:double*, $d as xs:integer) as xs:double*

map()

Map a function over the coordinates of a vector, returning the new vector.

Treat the vector as having a particular dimension.

##### Params

- f as function(xs:double)asxs:double: function from coordinate to coordinate
- vector as xs:double*: input vector
- d as xs:integer: the effective dimension of the vector

##### Returns

- xs:double*: application of function to coordinates

declare function this:map( $f as function(xs:double) as xs:double, $vector as xs:double*, $d as xs:integer ) as xs:double* { for-each(this:as-dimension($vector, $d), $f) }

####
__Function__: map2

declare function map2($f as function(xs:double, xs:double) as xs:double,
$p1 as xs:double*,
$p2 as xs:double*) as xs:double*

__Function__: map2

declare function map2($f as function(xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*) as xs:double*

map2()

Map a function over the coordinates of two vectors, pair by pair, returning

the new vector. That is, apply function to the x coordinates, then the

y coordinates, and so on.

##### Params

- f as function(xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
- p1 as xs:double*: one input vector
- p2 as xs:double*: another input vector

##### Returns

- xs:double*: application of function to each pair

declare function this:map2( $f as function(xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double* ) as xs:double* { util:zip($f, $p1, $p2) }

####
__Function__: map2

declare function map2($f as function(xs:double, xs:double) as xs:double,
$p1 as xs:double*,
$p2 as xs:double*,
$d as xs:integer) as xs:double*

__Function__: map2

declare function map2($f as function(xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $d as xs:integer) as xs:double*

map2()

Map a function over the coordinates of two vectors, pair by pair, returning

the new vector. That is, apply function to the x coordinates, then the

y coordinates, and so on. Treat the vector as having a particular dimension.

##### Params

- f as function(xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
- p1 as xs:double*: one input vector
- p2 as xs:double*: another input vector
- d as xs:integer: the effective dimension of the vectors

##### Returns

- xs:double*: application of function to each pair

declare function this:map2( $f as function(xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $d as xs:integer ) as xs:double* { util:zip($f, this:as-dimension($p1, $d), this:as-dimension($p2, $d)) }

####
__Function__: map3

declare function map3($f as function(xs:double, xs:double, xs:double) as xs:double,
$p1 as xs:double*,
$p2 as xs:double*,
$p3 as xs:double*) as xs:double*

__Function__: map3

declare function map3($f as function(xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*) as xs:double*

map3()

Map a function over the coordinates of three vectors, triple by triple,

returning the new vector. That is, apply function to the x coordinates,

then the y coordinates, and so on.

##### Params

- f as function(xs:double,xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
- p1 as xs:double*: one input vector
- p2 as xs:double*: another input vector
- p3 as xs:double*: third input vector

##### Returns

- xs:double*: application of function to each triple

declare function this:map3( $f as function(xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double* ) as xs:double* { util:zip( function ($a as xs:double, $fc as function(xs:double) as xs:double) as xs:double {$fc($a)}, $p3, util:zip( function ($a as xs:double, $b as xs:double) as function(*) { $f($a, $b, ?) }, $p1, $p2 ) ) }

####
__Function__: map3

declare function map3($f as function(xs:double, xs:double, xs:double) as xs:double,
$p1 as xs:double*,
$p2 as xs:double*,
$p3 as xs:double*,
$d as xs:integer) as xs:double*

__Function__: map3

declare function map3($f as function(xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $d as xs:integer) as xs:double*

map3()

Map a function over the coordinates of three vectors, triple by triple,

returning the new vector. That is, apply function to the x coordinates,

then the y coordinates, and so on. Treat the vector as having a

particular dimension.

##### Params

- f as function(xs:double,xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
- p1 as xs:double*: one input vector
- p2 as xs:double*: another input vector
- p3 as xs:double*: third input vector
- d as xs:integer: the effective dimension of the vectors

##### Returns

- xs:double*: application of function to each triple

declare function this:map3( $f as function(xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $d as xs:integer ) as xs:double* { util:zip( function ($a as xs:double, $fc as function(xs:double) as xs:double) as xs:double {$fc($a)}, this:as-dimension($p3, $d), util:zip( function ($a as xs:double, $b as xs:double) as function(*) { $f($a, $b, ?) }, this:as-dimension($p1, $d), this:as-dimension($p2, $d) ) ) }

####
__Function__: map4

declare function map4($f as function(xs:double, xs:double, xs:double, xs:double) as xs:double,
$p1 as xs:double*,
$p2 as xs:double*,
$p3 as xs:double*,
$p4 as xs:double*) as xs:double*

__Function__: map4

declare function map4($f as function(xs:double, xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $p4 as xs:double*) as xs:double*

map4()

Map a function over the coordinates of four vectors, coordinate by

coordinate, returning the new vector. That is, apply function to the x

coordinates, then the y coordinates, and so on.

##### Params

- f as function(xs:double,xs:double,xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
- p1 as xs:double*: one input vector
- p2 as xs:double*: another input vector
- p3 as xs:double*: third input vector
- p4 as xs:double*: fourth input vector

##### Returns

- xs:double*: application of function to each quad

declare function this:map4( $f as function(xs:double, xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $p4 as xs:double* ) as xs:double* { util:zip( function ($d as xs:double, $fd as function(xs:double) as xs:double) as xs:double {$fd($d)}, $p4, util:zip( function ($c as xs:double, $fc as function(xs:double, xs:double) as xs:double) as function(*) {$fc($c, ?)}, $p3, util:zip( function ($a as xs:double, $b as xs:double) as function(*) { $f($a, $b, ?, ?) }, $p1, $p2 ) ) ) }

####
__Function__: map4

declare function map4($f as function(xs:double, xs:double, xs:double, xs:double) as xs:double,
$p1 as xs:double*,
$p2 as xs:double*,
$p3 as xs:double*,
$p4 as xs:double*,
$dim as xs:integer) as xs:double*

__Function__: map4

declare function map4($f as function(xs:double, xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $p4 as xs:double*, $dim as xs:integer) as xs:double*

map4()

Map a function over the coordinates of four vectors, coordinate by

coordinate, returning the new vector. That is, apply function to the x

coordinates, then the y coordinates, and so on. Treat the vector as having a

particular dimension.

##### Params

- f as function(xs:double,xs:double,xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
- p1 as xs:double*: one input vector
- p2 as xs:double*: another input vector
- p3 as xs:double*: third input vector
- p4 as xs:double*: fourth input vector
- dim as xs:integer

##### Returns

- xs:double*: application of function to each quad

declare function this:map4( $f as function(xs:double, xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $p4 as xs:double*, $dim as xs:integer ) as xs:double* { util:zip( function ($d as xs:double, $fd as function(xs:double) as xs:double) as xs:double {$fd($d)}, this:as-dimension($p4, $dim), util:zip( function ($c as xs:double, $fc as function(xs:double, xs:double) as xs:double) as function(*) {$fc($c, ?)}, this:as-dimension($p3, $dim), util:zip( function ($a as xs:double, $b as xs:double) as function(*) { $f($a, $b, ?, ?) }, this:as-dimension($p1, $dim), this:as-dimension($p2, $dim) ) ) ) }

####
__Function__: px

declare function px($vector as xs:double*) as xs:double

__Function__: px

declare function px($vector as xs:double*) as xs:double

px()

Accessor for the raw precision x coordinate

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:double: raw x coordinate

declare function this:px($vector as xs:double*) as xs:double { head(($vector[1],0E0)) }

####
__Function__: py

declare function py($vector as xs:double*) as xs:double

__Function__: py

declare function py($vector as xs:double*) as xs:double

py()

Accessor for the raw precision y coordinate

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:double: raw y coordinate

declare function this:py($vector as xs:double*) as xs:double { head(($vector[2],0E0)) }

####
__Function__: pz

declare function pz($vector as xs:double*) as xs:double

__Function__: pz

declare function pz($vector as xs:double*) as xs:double

pz()

Accessor for the raw precision z coordinate; returns 0 for 2D vector

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:double: raw z coordinate

declare function this:pz($vector as xs:double*) as xs:double { head(($vector[3],0E0)) }

####
__Function__: pw

declare function pw($vector as xs:double*) as xs:double

__Function__: pw

declare function pw($vector as xs:double*) as xs:double

pw()

Accessor for the raw precision w coordinate; returns 0 for 2D or 3D vector

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:double: raw w coordinate

declare function this:pw($vector as xs:double*) as xs:double { head(($vector[4],0E0)) }

####
__Function__: pxy

declare function pxy($vector as xs:double*) as xs:double*

__Function__: pxy

declare function pxy($vector as xs:double*) as xs:double*

pxy()

Accessor for the raw precision xy vector.

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:double*: new vector of just the raw x and y coordinates

declare function this:pxy($vector as xs:double*) as xs:double* { head(($vector[1],0E0)), head(($vector[1],0E0)) }

####
__Function__: pxz

declare function pxz($vector as xs:double*) as xs:double*

__Function__: pxz

declare function pxz($vector as xs:double*) as xs:double*

pxz()

Accessor for the raw precision xz vector.

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:double*: new vector of just the raw x and z coordinates

declare function this:pxz($vector as xs:double*) as xs:double* { head(($vector[1],0E0)), head(($vector[3],0E0)) }

####
__Function__: x

declare function x($vector as xs:double*) as xs:integer

__Function__: x

declare function x($vector as xs:double*) as xs:integer

x()

Accessor for the snapped x coordinate

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:integer: x coordinate, rounded

declare function this:x($vector as xs:double*) as xs:integer { let $x := head(($vector[1],0)) return ( if ($x = xs:double("INF")) then $util:UINT64_MAX else if ($x = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($x) cast as xs:integer ) }

####
__Function__: y

declare function y($vector as xs:double*) as xs:integer

__Function__: y

declare function y($vector as xs:double*) as xs:integer

y()

Accessor for the snapped y coordinate

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:integer: y coordinate, rounded

declare function this:y($vector as xs:double*) as xs:integer { let $y := head(($vector[2],0)) return ( if ($y = xs:double("INF")) then $util:UINT64_MAX else if ($y = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($y) cast as xs:integer ) }

####
__Function__: z

declare function z($vector as xs:double*) as xs:integer

__Function__: z

declare function z($vector as xs:double*) as xs:integer

z()

Accessor for the snapped z coordinate

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:integer: z coordinate, rounded

declare function this:z($vector as xs:double*) as xs:integer { let $z := head(($vector[3],0)) return ( if ($z = xs:double("INF")) then $util:UINT64_MAX else if ($z = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($z) cast as xs:integer ) }

####
__Function__: w

declare function w($vector as xs:double*) as xs:integer

__Function__: w

declare function w($vector as xs:double*) as xs:integer

w()

Accessor for the snapped w coordinate

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:integer: w coordinate, rounded

declare function this:w($vector as xs:double*) as xs:integer { let $w := head(($vector[4],0)) return ( if ($w = xs:double("INF")) then $util:UINT64_MAX else if ($w = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($w) cast as xs:integer ) }

####
__Function__: magnitude

declare function magnitude($v as xs:double*) as xs:double

__Function__: magnitude

declare function magnitude($v as xs:double*) as xs:double

magnitude()

Magnitude of the vector from origin.

##### Params

- v as xs:double*: the vector

##### Returns

- xs:double: Euclidean length of vector

declare function this:magnitude($v as xs:double*) as xs:double { math:sqrt( fn:sum(for $c in $v return $c*$c) ) }

####
__Function__: dot

declare function dot($p1 as xs:double*, $p2 as xs:double*) as xs:double

__Function__: dot

declare function dot($p1 as xs:double*, $p2 as xs:double*) as xs:double

dot()

Compute the dot product of two vectors

##### Params

- p1 as xs:double*: one vector
- p2 as xs:double*: another vector

##### Returns

- xs:double: p1·p2

declare function this:dot($p1 as xs:double*, $p2 as xs:double*) as xs:double { fn:sum( this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a * $b}, $p1, $p2) ) }

####
__Function__: dot2

declare function dot2($p1 as xs:double*) as xs:double

__Function__: dot2

declare function dot2($p1 as xs:double*) as xs:double

dot2()

Compute the dot product of a vector with itself

##### Params

- p1 as xs:double*

##### Returns

- xs:double: p·p

declare function this:dot2($p1 as xs:double*) as xs:double { fn:sum( this:map(function ($a as xs:double) as xs:double {$a * $a}, $p1) ) }

####
__Function__: ndot

declare function ndot($p1 as xs:double*, $p2 as xs:double*) as xs:double

__Function__: ndot

declare function ndot($p1 as xs:double*, $p2 as xs:double*) as xs:double

Negated dot: useful for some of the SDF functions.

##### Params

- p1 as xs:double*: one vector
- p2 as xs:double*: another vector

##### Returns

- xs:double: p1·-p2

declare function this:ndot($p1 as xs:double*, $p2 as xs:double*) as xs:double { fn:sum( this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a * -$b}, $p1, $p2) ) }

####
__Function__: mix

declare function mix($u as xs:double*,
$v as xs:double*,
$f as xs:double) as xs:double*

__Function__: mix

declare function mix($u as xs:double*, $v as xs:double*, $f as xs:double) as xs:double*

Linear combination of two vectors, coordinate by coordinate.

Generally $f in in [0,1], but you can

get extrapolations with numbers outside that range.

##### Params

- u as xs:double*: one vector
- v as xs:double*: other vector
- f as xs:double: combination fraction

##### Returns

- xs:double*: vector of linear combination of each coordinate pair

declare function this:mix( $u as xs:double*, $v as xs:double*, $f as xs:double ) as xs:double* { (: Logically $a + ($b - $a) * $f but better endvector behaviour :) this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a * (1 - $f) + $b * $f}, $u, $v) }

####
__Function__: determinant

declare function determinant($u as xs:double*, $v as xs:double*) as xs:double

__Function__: determinant

declare function determinant($u as xs:double*, $v as xs:double*) as xs:double

determinant()

Compute the determinant of two 2D vectors.

##### Params

- u as xs:double*: one vector
- v as xs:double*: another vector

##### Returns

- xs:double

declare function this:determinant($u as xs:double*, $v as xs:double*) as xs:double { this:px($u)*this:py($v) - this:py($u)*this:px($v) }

####
__Function__: cross

declare function cross($u as xs:double*, $v as xs:double*) as xs:double*

__Function__: cross

declare function cross($u as xs:double*, $v as xs:double*) as xs:double*

cross()

Compute the cross product of two 3D vectors.

##### Params

- u as xs:double*: one vector
- v as xs:double*: another vector

##### Returns

- xs:double*: uXv

declare function this:cross($u as xs:double*, $v as xs:double*) as xs:double* { this:py($u)*this:pz($v) - this:pz($u)*this:py($v), this:pz($u)*this:px($v) - this:px($u)*this:pz($v), this:px($u)*this:py($v) - this:py($u)*this:px($v) }

####
__Function__: round

declare function round($v as xs:double*) as xs:double*

__Function__: round

declare function round($v as xs:double*) as xs:double*

round()

Round the coordinates of the vector.

##### Params

- v as xs:double*: vector

##### Returns

- xs:double*: rounded vector

declare function this:round($v as xs:double*) as xs:double* { this:map(function ($c as xs:double) as xs:double {fn:round($c)}, $v) }

####
__Function__: times

declare function times($v as xs:double*, $k as xs:double) as xs:double*

__Function__: times

declare function times($v as xs:double*, $k as xs:double) as xs:double*

times()

Scale every coordinate by a constant, returning the new vector.

##### Params

- v as xs:double*
- k as xs:double: constant multiplier

##### Returns

- xs:double*: scaled vector

declare function this:times($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($c as xs:double) as xs:double {$k * $c}, $v) }

####
__Function__: multiply

declare function multiply($u as xs:double*, $v as xs:double*) as xs:double*

__Function__: multiply

declare function multiply($u as xs:double*, $v as xs:double*) as xs:double*

multiply()

Coordinate-by-coordinate multiplication.

##### Params

- u as xs:double*: one vector
- v as xs:double*: another vector

##### Returns

- xs:double*: new vector

declare function this:multiply($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a * $b}, $u, $v) }

####
__Function__: div

declare function div($v as xs:double*, $k as xs:double) as xs:double*

__Function__: div

declare function div($v as xs:double*, $k as xs:double) as xs:double*

div()

Divide every coordinate by a constant, returning the new vector.

##### Params

- v as xs:double*
- k as xs:double: constant multiplier

##### Returns

- xs:double*: scaled vector

declare function this:div($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($c as xs:double) as xs:double {($c div $k) cast as xs:double}, $v) }

####
__Function__: divide

declare function divide($u as xs:double*, $v as xs:double*) as xs:double*

__Function__: divide

declare function divide($u as xs:double*, $v as xs:double*) as xs:double*

divide()

Coordinate-by-coordinate division

##### Params

- u as xs:double*: one vector
- v as xs:double*: another vector

##### Returns

- xs:double*: new vector

declare function this:divide($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a div $b}, $u, $v) }

####
__Function__: mod

declare function mod($v as xs:double*, $k as xs:double) as xs:double*

__Function__: mod

declare function mod($v as xs:double*, $k as xs:double) as xs:double*

mod()

Compute the mod of every coordinate by a constant, returning the new vector.

##### Params

- v as xs:double*
- k as xs:double: constant modulus

##### Returns

- xs:double*: new vector

declare function this:mod($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($c as xs:double) as xs:double {$c mod $k}, $v) }

####
__Function__: modulo

declare function modulo($u as xs:double*, $v as xs:double*) as xs:double*

__Function__: modulo

declare function modulo($u as xs:double*, $v as xs:double*) as xs:double*

divide()

Coordinate-by-coordinate modulo.

##### Params

- u as xs:double*: one vector
- v as xs:double*: another vector

##### Returns

- xs:double*: new vector

declare function this:modulo($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a mod $b}, $u, $v) }

####
__Function__: add

declare function add($u as xs:double*, $v as xs:double*) as xs:double*

__Function__: add

declare function add($u as xs:double*, $v as xs:double*) as xs:double*

add()

Add two vectors, returning the new vector.

##### Params

- u as xs:double*: one vector
- v as xs:double*: the other vector

##### Returns

- xs:double*: u+v

declare function this:add($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a + $b}, $u, $v) }

####
__Function__: plus

declare function plus($v as xs:double*, $k as xs:double) as xs:double*

__Function__: plus

declare function plus($v as xs:double*, $k as xs:double) as xs:double*

plus()

Add a constant to every coordinate in a vector, returning the new vector.

##### Params

- v as xs:double*: the vector
- k as xs:double: constant to add

##### Returns

- xs:double*: new vector

declare function this:plus($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {$a + $k}, $v) }

####
__Function__: minus

declare function minus($v as xs:double*) as xs:double*

__Function__: minus

declare function minus($v as xs:double*) as xs:double*

minus()

Negate the coordinates of the vector, returning the new vector.

##### Params

- v as xs:double*: the vector

##### Returns

- xs:double*: -v

declare function this:minus($v as xs:double*) as xs:double* { this:map(function ($a as xs:double) as xs:double {-$a}, $v) }

####
__Function__: minus

declare function minus($v as xs:double*, $k as xs:double) as xs:double*

__Function__: minus

declare function minus($v as xs:double*, $k as xs:double) as xs:double*

minus()

Subtract a constant to every coordinate in a vector, returning the new vector.

##### Params

- v as xs:double*: the vector
- k as xs:double: constant to subtract

##### Returns

- xs:double*: new vector

declare function this:minus($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {$a - $k}, $v) }

####
__Function__: sub

declare function sub($u as xs:double*, $v as xs:double*) as xs:double*

__Function__: sub

declare function sub($u as xs:double*, $v as xs:double*) as xs:double*

sub()

Subtract one vector from another, returning the new vector.

##### Params

- u as xs:double*: one vector
- v as xs:double*: the other vector

##### Returns

- xs:double*: u-v

declare function this:sub($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a - $b}, $u, $v) }

####
__Function__: abs

declare function abs($v as xs:double*) as xs:double*

__Function__: abs

declare function abs($v as xs:double*) as xs:double*

abs()

Coordinate-by-coordinate absolute value.

##### Params

- v as xs:double*: vector

##### Returns

- xs:double*: new vector

declare function this:abs($v as xs:double*) as xs:double* { this:map(function ($a as xs:double) as xs:double {fn:abs($a)}, $v) }

####
__Function__: sign

declare function sign($v as xs:double*) as xs:double*

__Function__: sign

declare function sign($v as xs:double*) as xs:double*

sign()

Coordinate-by-coordinate sign.

##### Params

- v as xs:double*: vector

##### Returns

- xs:double*: new vector

declare function this:sign($v as xs:double*) as xs:double* { this:map( function ($a as xs:double) as xs:double { if ($a = 0) then 0E0 else if ($a < 0) then -1E0 else 1E0 }, $v ) }

####
__Function__: min

declare function min($u as xs:double*, $v as xs:double*) as xs:double*

__Function__: min

declare function min($u as xs:double*, $v as xs:double*) as xs:double*

min()

Coordinate-by-coordinate minimum: minimum of each coordinate pair.

##### Params

- u as xs:double*: one vector
- v as xs:double*: another vector

##### Returns

- xs:double*: new vector

declare function this:min($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {fn:min(($a,$b))}, $u, $v) }

####
__Function__: max

declare function max($u as xs:double*, $v as xs:double*) as xs:double*

__Function__: max

declare function max($u as xs:double*, $v as xs:double*) as xs:double*

max()

Coordinate-by-coordinate maximum: maximum of each coordinate pair.

##### Params

- u as xs:double*: one vector
- v as xs:double*: another vector

##### Returns

- xs:double*: new vector

declare function this:max($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {fn:max(($a,$b))}, $u, $v) }

####
__Function__: at-min

declare function at-min($v as xs:double*, $val as xs:double) as xs:double*

__Function__: at-min

declare function at-min($v as xs:double*, $val as xs:double) as xs:double*

at-min()

Coordinate-by-coordinate minimum: minimum of each coordinate with value.

##### Params

- v as xs:double*: vectoral: comparison value
- val as xs:double: comparison value

##### Returns

- xs:double*: new vector

declare function this:at-min($v as xs:double*, $val as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {fn:min(($a, $val))}, $v) }

####
__Function__: at-max

declare function at-max($v as xs:double*, $val as xs:double) as xs:double*

__Function__: at-max

declare function at-max($v as xs:double*, $val as xs:double) as xs:double*

at-max()

Coordinate-by-coordinate maximum: maximum of each coordinate with value.

##### Params

- v as xs:double*: vectoral: comparison value
- val as xs:double: comparison value

##### Returns

- xs:double*: new vector

declare function this:at-max($v as xs:double*, $val as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {fn:max(($a, $val))}, $v) }

####
__Function__: normalize

declare function normalize($v as xs:double*) as xs:double*

__Function__: normalize

declare function normalize($v as xs:double*) as xs:double*

normalize()

Normalize a vector represented as a vector (scale to unit vector), returning

the new vector.

##### Params

- v as xs:double*: the vector

##### Returns

- xs:double*: normalized vector

declare function this:normalize($v as xs:double*) as xs:double* { let $l := this:magnitude($v) return if ($l = 0) then $v else this:map(function ($c as xs:double) as xs:double {$c div $l}, $v) }

####
__Function__: clamp

declare function clamp($v as xs:double*, $min as xs:double, $max as xs:double) as xs:double*

__Function__: clamp

declare function clamp($v as xs:double*, $min as xs:double, $max as xs:double) as xs:double*

clamp()

Coordinate-by-coordinate clamping.

##### Params

- v as xs:double*: vector
- min as xs:double: minimum value
- max as xs:double: maximum value

##### Returns

- xs:double*: new vector

declare function this:clamp($v as xs:double*, $min as xs:double, $max as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {util:clamp($a, $min, $max)}, $v) }

####
__Function__: clampv

declare function clampv($v as xs:double*, $minv as xs:double*, $maxv as xs:double*) as xs:double*

__Function__: clampv

declare function clampv($v as xs:double*, $minv as xs:double*, $maxv as xs:double*) as xs:double*

clampv()

Coordinate-by-coordinate clamping: clamp with corresponding coordinates of

min/max vectors.

##### Params

- v as xs:double*: vector
- minv as xs:double*: minimum values
- maxv as xs:double*: maximum values

##### Returns

- xs:double*: new vector

declare function this:clampv($v as xs:double*, $minv as xs:double*, $maxv as xs:double*) as xs:double* { this:map3(function ($a as xs:double, $min as xs:double?, $max as xs:double?) as xs:double {util:clamp($a, $min, $max)}, $v, $minv, $maxv) }

####
__Function__: perpendicular

declare function perpendicular($v as xs:double*) as xs:double*

__Function__: perpendicular

declare function perpendicular($v as xs:double*) as xs:double*

perpendicular()

Compute the perpendicular vector for a 2D vector represented as a vector.

(Rotated by 90 degrees.)

##### Params

- v as xs:double*: the vector

##### Returns

- xs:double*: perpendicular vector

declare function this:perpendicular($v as xs:double*) as xs:double* { (this:py($v), -this:px($v)) }

####
__Function__: snap

declare function snap($vector as xs:double*) as xs:integer*

__Function__: snap

declare function snap($vector as xs:double*) as xs:integer*

snap()

Snap the coordinates of the vector, returning the vector with snapped

(i.e. integer) coordinates. NaN snaps to UINT64_MAX.

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:integer*

declare function this:snap($vector as xs:double*) as xs:integer* { for-each($vector, function ($c as xs:double) as xs:integer { if ($c = xs:double("INF") or not($c = $c)) then $util:UINT64_MAX else if ($c = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($c) cast as xs:integer } ) }

####
__Function__: floor

declare function floor($vector as xs:double*) as xs:integer*

__Function__: floor

declare function floor($vector as xs:double*) as xs:integer*

floor()

Return vector where all the coordinates have been set to the floor of

the original vector's values. Differs from snap() for negative coordinates.

##### Params

- vector as xs:double*: the vector

##### Returns

- xs:integer*

declare function this:floor($vector as xs:double*) as xs:integer* { for-each($vector, function ($c as xs:double) as xs:integer {fn:floor($c) cast as xs:integer}) }

####
__Function__: decimal

declare function decimal($vector as xs:double*, $digits as xs:integer) as xs:double*

__Function__: decimal

declare function decimal($vector as xs:double*, $digits as xs:integer) as xs:double*

decimal()

Perform decimal rounding on all the coordinates (see util:decimal).

##### Params

- vector as xs:double*: the vector to round
- digits as xs:integer: how many digits after the decimal vector to keep

##### Returns

- xs:double*

declare function this:decimal($vector as xs:double*, $digits as xs:integer) as xs:double* { for-each($vector, function ($c as xs:double) as xs:double {util:decimal($c, $digits)}) }

####
__Function__: quote

declare function quote($vector as xs:double*) as xs:string

__Function__: quote

declare function quote($vector as xs:double*) as xs:string

quote()

Return a string value for the vector, suitable for debugging.

##### Params

- vector as xs:double*: the vector to quote

##### Returns

- xs:string

declare function this:quote($vector as xs:double*) as xs:string { string-join($vector!string(.),",") }

####
__Function__: valid

declare function valid($point as xs:double*) as xs:boolean

__Function__: valid

declare function valid($point as xs:double*) as xs:boolean

valid()

False if a coordinate is INF or NaN

##### Params

- point as xs:double*

##### Returns

- xs:boolean

declare function this:valid($point as xs:double*) as xs:boolean { every $c in $point satisfies $c=$c and not($c=(xs:double("INF"),xs:double("-INF"))) }

####
__Function__: same

declare function same($this as xs:double*, $other as xs:double*) as xs:boolean

__Function__: same

declare function same($this as xs:double*, $other as xs:double*) as xs:boolean

same()

Equality comparison for vectors, ignoring annotation properties.

Return true() if they have equal coordinates.

##### Params

- this as xs:double*: one vector
- other as xs:double*: the vector to compare it to

##### Returns

- xs:boolean: this=other

declare function this:same($this as xs:double*, $other as xs:double*) as xs:boolean { let $d := max((count($this), count($other))) return ( deep-equal( this:as-dimension($this, $d), this:as-dimension($other, $d) ) ) }

####
__Function__: distance

declare function distance($v1 as xs:double*, $v2 as xs:double*) as xs:double

__Function__: distance

declare function distance($v1 as xs:double*, $v2 as xs:double*) as xs:double

distance()

Euclidean distance between vectors

##### Params

- v1 as xs:double*: one vector
- v2 as xs:double*: another vector

##### Returns

- xs:double: distance(v1,v2)

declare function this:distance($v1 as xs:double*, $v2 as xs:double*) as xs:double { let $d := fn:max((this:dimension($v1), this:dimension($v2))) return ( math:sqrt( fn:sum( util:zip( function ($c1 as xs:double, $c2 as xs:double) as xs:double { ($c1 - $c2)*($c1 - $c2) }, this:as-dimension($v1, $d), this:as-dimension($v2, $d) ) ) ) ) }

####
__Function__: polar-distance

declare function polar-distance($p as xs:double*,
$q as xs:double*) as xs:double

__Function__: polar-distance

declare function polar-distance($p as xs:double*, $q as xs:double*) as xs:double

polar-distance()

Angular distance between points taken as vectors.

##### Params

- p as xs:double*
- q as xs:double*

##### Returns

- xs:double

declare function this:polar-distance( $p as xs:double*, $q as xs:double* ) as xs:double { abs(this:angle((0,0), $p) - this:angle((0,0), $q)) }

####
__Function__: taxi-distance

declare function taxi-distance($p as xs:double*,
$q as xs:double*) as xs:double

__Function__: taxi-distance

declare function taxi-distance($p as xs:double*, $q as xs:double*) as xs:double

taxi-distance()

Taxi-cab distance between two points. That is, right angle distance, such as

a taxi going 2 blocks North plus 3 blocks East = distance 5 blocks.

##### Params

- p as xs:double*
- q as xs:double*

##### Returns

- xs:double

declare function this:taxi-distance( $p as xs:double*, $q as xs:double* ) as xs:double { let $d := max((count($p), count($q))) return ( sum( util:zip( function ($cp as xs:double, $cq as xs:double) as xs:double { abs($cp - $cq) }, this:as-dimension($p, $d), this:as-dimension($q, $d) ) ) ) }

####
__Function__: avg-distance

declare function avg-distance($p as xs:double*,
$q as xs:double*) as xs:double

__Function__: avg-distance

declare function avg-distance($p as xs:double*, $q as xs:double*) as xs:double

avg-distance()

Average of distances along each dimension.

##### Params

- p as xs:double*
- q as xs:double*

##### Returns

- xs:double

declare function this:avg-distance( $p as xs:double*, $q as xs:double* ) as xs:double { let $d := max((count($p), count($q))) return ( avg( util:zip( function ($cp as xs:double, $cq as xs:double) as xs:double { abs($cp - $cq) }, this:as-dimension($p, $d), this:as-dimension($q, $d) ) ) ) }

####
__Function__: rail-distance

declare function rail-distance($p as xs:double*,
$q as xs:double*) as xs:double

__Function__: rail-distance

declare function rail-distance($p as xs:double*, $q as xs:double*) as xs:double

rail-distance()

Railway distance, e.g distance to the central hub and back out.

The hub is the origin.

##### Params

- p as xs:double*
- q as xs:double*

##### Returns

- xs:double

declare function this:rail-distance( $p as xs:double*, $q as xs:double* ) as xs:double { this:distance($p, (0,0)) + this:distance($q, (0,0)) }

####
__Function__: rail-distance

declare function rail-distance($p as xs:double*,
$q as xs:double*,
$origin as xs:double*) as xs:double

__Function__: rail-distance

declare function rail-distance($p as xs:double*, $q as xs:double*, $origin as xs:double*) as xs:double

rail-distance()

Railway distance, e.g distance to the central hub and back out where the hub

is specified.

##### Params

- p as xs:double*
- q as xs:double*
- origin as xs:double*

##### Returns

- xs:double

declare function this:rail-distance( $p as xs:double*, $q as xs:double*, $origin as xs:double* ) as xs:double { this:distance($p, $origin) + this:distance($q, $origin) }

####
__Function__: chebyshev-distance

declare function chebyshev-distance($p as xs:double*,
$q as xs:double*) as xs:double

__Function__: chebyshev-distance

declare function chebyshev-distance($p as xs:double*, $q as xs:double*) as xs:double

chebyshev-distance()

Chebyshev distance metric. The maximum of the dimensional distances.

##### Params

- p as xs:double*
- q as xs:double*

##### Returns

- xs:double

declare function this:chebyshev-distance( $p as xs:double*, $q as xs:double* ) as xs:double { let $d := max((count($p), count($q))) return ( max( util:zip( function ($cp as xs:double, $cq as xs:double) as xs:double { abs($cp - $cq) }, this:as-dimension($p, $d), this:as-dimension($q, $d) ) ) ) }

####
__Function__: minkowski-distance

declare function minkowski-distance($p as xs:double*,
$q as xs:double*,
$order as xs:double) as xs:double

__Function__: minkowski-distance

declare function minkowski-distance($p as xs:double*, $q as xs:double*, $order as xs:double) as xs:double

minwokski-distance()

Minkowski distance metric of the given order. Certain orders give rise to

other metrics:

order=1=>taxi; order=2=>Euclidean; as order=>∞=>Chebyshev;

order<1 is not a metric unless undo the final pow. i.e. X is a metric but

X^(1/order) is not.

##### Params

- p as xs:double*
- q as xs:double*
- order as xs:double

##### Returns

- xs:double

declare function this:minkowski-distance( $p as xs:double*, $q as xs:double*, $order as xs:double ) as xs:double { let $d := max((count($p), count($q))) return ( math:pow( sum( util:zip( function ($cp as xs:double, $cq as xs:double) as xs:double { math:pow(abs($cp - $cq), $order) }, this:as-dimension($p, $d), this:as-dimension($q, $d) ) ), 1 div $order ) ) }

####
__Function__: cosine-similarity

declare function cosine-similarity($p as xs:double*,
$q as xs:double*) as xs:double

__Function__: cosine-similarity

declare function cosine-similarity($p as xs:double*, $q as xs:double*) as xs:double

cosine-similarity()

Cosine similarity: (A·B)/(∥A∥∥B∥) = ΣAiBi/(√Ai²√Bi²)

##### Params

- p as xs:double*
- q as xs:double*

##### Returns

- xs:double

declare function this:cosine-similarity( $p as xs:double*, $q as xs:double* ) as xs:double { this:dot($p, $q) div (this:magnitude($p)*this:magnitude($q)) }

####
__Function__: cosine-distance

declare function cosine-distance($p as xs:double*,
$q as xs:double*) as xs:double

__Function__: cosine-distance

declare function cosine-distance($p as xs:double*, $q as xs:double*) as xs:double

cosine-distance()

Cosine similarity as a metric: acos(similarity)/π => [0,1]

##### Params

- p as xs:double*
- q as xs:double*

##### Returns

- xs:double

declare function this:cosine-distance( $p as xs:double*, $q as xs:double* ) as xs:double { math:acos(this:cosine-similarity($p, $q)) div math:pi() }

####
__Function__: angle

declare function angle($last as xs:double*, $curr as xs:double*) as xs:double

__Function__: angle

declare function angle($last as xs:double*, $curr as xs:double*) as xs:double

angle()

Compute the angle (azimuth) from one point to the next, in degrees

Return 0 if points are the same

##### Params

- last as xs:double*: previous point; use (0,0) if no previous
- curr as xs:double*: current point

##### Returns

- xs:double

declare function this:angle($last as xs:double*, $curr as xs:double*) as xs:double { let $this := $curr=>this:as-dimension(2) let $prev := if (empty($last)) then (0,0) else this:as-dimension($last,2) return if (this:same($prev, $this) or (this:distance($prev,$this) < $config:ε)) then 0 else if (this:px($this)=this:px($prev)) then ( if (this:py($prev) > this:py($this)) then 270 (: remapped -90 :) else 90 ) else ( util:remap-degrees(util:degrees( (math:pi() div 2) - math:atan2(this:px($this) - this:px($prev), this:py($this) - this:py($prev)) )) ) }

####
__Function__: inclination

declare function inclination($last as xs:double*, $curr as xs:double*) as xs:double

__Function__: inclination

declare function inclination($last as xs:double*, $curr as xs:double*) as xs:double

inclination()

Compute the inclination angle from one point in the next, in degrees

Return 90 if the points are the same

##### Params

- last as xs:double*: previous point; use (0,0,0) if no previous
- curr as xs:double*: current point

##### Returns

- xs:double

declare function this:inclination($last as xs:double*, $curr as xs:double*) as xs:double { let $curr := $curr=>this:as-dimension(3) let $prev := if (empty($last)) then (0,0,0) else $last=>this:as-dimension(3) let $d := this:distance($prev,$curr) return if (this:same($prev,$curr) or ($d < $config:ε)) then 90 else ( util:remap-degrees(util:degrees( math:acos( (this:pz($curr) - this:pz($prev)) div $d ) )) ) }

####
__Function__: destination

declare function destination($point as xs:double*,
$degrees as xs:double,
$length as xs:double) as xs:double*

__Function__: destination

declare function destination($point as xs:double*, $degrees as xs:double, $length as xs:double) as xs:double*

destination()

Point at a particular distance and direction.

##### Params

- point as xs:double*: starting point
- degrees as xs:double: bearing from point
- length as xs:double: how far along bearing to travel

##### Returns

- xs:double*

declare function this:destination( $point as xs:double*, $degrees as xs:double, $length as xs:double ) as xs:double* { let $angle := util:radians($degrees) return ( this:px($point) + math:cos($angle)*$length, this:py($point) + math:sin($angle)*$length ) }

####
__Function__: mutate

declare function mutate($vectors as xs:double*,
$mutate as function(xs:double*) as xs:double*) as xs:double*

__Function__: mutate

declare function mutate($vectors as xs:double*, $mutate as function(xs:double*) as xs:double*) as xs:double*

mutate()

Mutate a sequence of 2D vectors using a mutation function.

##### Params

- vectors as xs:double*: the vectors
- mutate as function(xs:double*)asxs:double*: function that takes a vector as an argument and returns a new vector

##### Returns

- xs:double*

declare function this:mutate( $vectors as xs:double*, $mutate as function(xs:double*) as xs:double* ) as xs:double* { for $i in 1 to count($vectors) idiv 2 return ( $mutate(($vectors[2*$i - 1], $vectors[2*$i])) ) }

### Original Source Code

xquery version "3.1"; (:~ : Vectors of numbers : : Copyright© Mary Holstege 2020-2023 : CC-BY (https://creativecommons.org/licenses/by/4.0/) : @since May 2021 : @custom:Status Incomplete :) module namespace this="http://mathling.com/core/vector"; import module namespace config="http://mathling.com/core/config" at "../core/config.xqy"; import module namespace errors="http://mathling.com/core/errors" at "../core/errors.xqy"; import module namespace util="http://mathling.com/core/utilities" at "../core/utilities.xqy"; declare namespace math="http://www.w3.org/2005/xpath-functions/math"; (:~ : as-dimension() : Cast the vector to a vector with the given number of dimensions. : If we are reducing dimensions, just remove excess coordinates. : If we are expanding dimensions, fill in with 0. : : @param $vector: source vector : @param $dim: new dimension of vector : @return new vector :) declare function this:as-dimension($vector as xs:double*, $dim as xs:integer) as xs:double* { ($vector, for $i in 1 to $dim return 0E0)[position() <= $dim] }; (:~ : unquote() : Parse a string representation of a vector and produce a vector. : The string consists of the coordinates in with commas and : optional spaces in between. : : @param $string: the input string to parse : @return vector :) declare function this:unquote($string as xs:string) as xs:double* { tokenize($string, "[ ]*,[ ]*")!xs:double(.) }; (:~ : dimension() : Return the dimension of the vector. : : @param $vector: the vector : @return length of vector :) declare function this:dimension($vector as xs:double*) as xs:integer { count($vector) }; (:~ : map() : Map a function over the coordinates of a vector, returning the new vector. : : @param $f: function from coordinate to coordinate : @param $vector: input vector : @return application of function to each coordinate :) declare function this:map( $f as function(xs:double) as xs:double, $vector as xs:double* ) as xs:double* { for-each($vector, $f) }; (:~ : map() : Map a function over the coordinates of a vector, returning the new vector. : Treat the vector as having a particular dimension. : : @param $f: function from coordinate to coordinate : @param $vector: input vector : @param $d: the effective dimension of the vector : @return application of function to coordinates :) declare function this:map( $f as function(xs:double) as xs:double, $vector as xs:double*, $d as xs:integer ) as xs:double* { for-each(this:as-dimension($vector, $d), $f) }; (:~ : map2() : Map a function over the coordinates of two vectors, pair by pair, returning : the new vector. That is, apply function to the x coordinates, then the : y coordinates, and so on. : : @param $f: function from coordinate pairs to coordinate : @param $p1: one input vector : @param $p2: another input vector : @return application of function to each pair :) declare function this:map2( $f as function(xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double* ) as xs:double* { util:zip($f, $p1, $p2) }; (:~ : map2() : Map a function over the coordinates of two vectors, pair by pair, returning : the new vector. That is, apply function to the x coordinates, then the : y coordinates, and so on. Treat the vector as having a particular dimension. : : @param $f: function from coordinate pairs to coordinate : @param $p1: one input vector : @param $p2: another input vector : @param $d: the effective dimension of the vectors : @return application of function to each pair :) declare function this:map2( $f as function(xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $d as xs:integer ) as xs:double* { util:zip($f, this:as-dimension($p1, $d), this:as-dimension($p2, $d)) }; (:~ : map3() : Map a function over the coordinates of three vectors, triple by triple, : returning the new vector. That is, apply function to the x coordinates, : then the y coordinates, and so on. : : @param $f: function from coordinate pairs to coordinate : @param $p1: one input vector : @param $p2: another input vector : @param $p3: third input vector : @return application of function to each triple :) declare function this:map3( $f as function(xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double* ) as xs:double* { util:zip( function ($a as xs:double, $fc as function(xs:double) as xs:double) as xs:double {$fc($a)}, $p3, util:zip( function ($a as xs:double, $b as xs:double) as function(*) { $f($a, $b, ?) }, $p1, $p2 ) ) }; (:~ : map3() : Map a function over the coordinates of three vectors, triple by triple, : returning the new vector. That is, apply function to the x coordinates, : then the y coordinates, and so on. Treat the vector as having a : particular dimension. : : @param $f: function from coordinate pairs to coordinate : @param $p1: one input vector : @param $p2: another input vector : @param $p3: third input vector : @param $d: the effective dimension of the vectors : @return application of function to each triple :) declare function this:map3( $f as function(xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $d as xs:integer ) as xs:double* { util:zip( function ($a as xs:double, $fc as function(xs:double) as xs:double) as xs:double {$fc($a)}, this:as-dimension($p3, $d), util:zip( function ($a as xs:double, $b as xs:double) as function(*) { $f($a, $b, ?) }, this:as-dimension($p1, $d), this:as-dimension($p2, $d) ) ) }; (:~ : map4() : Map a function over the coordinates of four vectors, coordinate by : coordinate, returning the new vector. That is, apply function to the x : coordinates, then the y coordinates, and so on. : : @param $f: function from coordinate pairs to coordinate : @param $p1: one input vector : @param $p2: another input vector : @param $p3: third input vector : @param $p4: fourth input vector : @return application of function to each quad :) declare function this:map4( $f as function(xs:double, xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $p4 as xs:double* ) as xs:double* { util:zip( function ($d as xs:double, $fd as function(xs:double) as xs:double) as xs:double {$fd($d)}, $p4, util:zip( function ($c as xs:double, $fc as function(xs:double, xs:double) as xs:double) as function(*) {$fc($c, ?)}, $p3, util:zip( function ($a as xs:double, $b as xs:double) as function(*) { $f($a, $b, ?, ?) }, $p1, $p2 ) ) ) }; (:~ : map4() : Map a function over the coordinates of four vectors, coordinate by : coordinate, returning the new vector. That is, apply function to the x : coordinates, then the y coordinates, and so on. Treat the vector as having a : particular dimension. : : @param $f: function from coordinate pairs to coordinate : @param $p1: one input vector : @param $p2: another input vector : @param $p3: third input vector : @param $p4: fourth input vector : @param $d: the effective dimension of the vectors : @return application of function to each quad :) declare function this:map4( $f as function(xs:double, xs:double, xs:double, xs:double) as xs:double, $p1 as xs:double*, $p2 as xs:double*, $p3 as xs:double*, $p4 as xs:double*, $dim as xs:integer ) as xs:double* { util:zip( function ($d as xs:double, $fd as function(xs:double) as xs:double) as xs:double {$fd($d)}, this:as-dimension($p4, $dim), util:zip( function ($c as xs:double, $fc as function(xs:double, xs:double) as xs:double) as function(*) {$fc($c, ?)}, this:as-dimension($p3, $dim), util:zip( function ($a as xs:double, $b as xs:double) as function(*) { $f($a, $b, ?, ?) }, this:as-dimension($p1, $dim), this:as-dimension($p2, $dim) ) ) ) }; (:~ : px() : Accessor for the raw precision x coordinate : : @param $vector: the vector : @return raw x coordinate :) declare function this:px($vector as xs:double*) as xs:double { head(($vector[1],0E0)) }; (:~ : py() : Accessor for the raw precision y coordinate : : @param $vector: the vector : @return raw y coordinate :) declare function this:py($vector as xs:double*) as xs:double { head(($vector[2],0E0)) }; (:~ : pz() : Accessor for the raw precision z coordinate; returns 0 for 2D vector : : @param $vector: the vector : @return raw z coordinate :) declare function this:pz($vector as xs:double*) as xs:double { head(($vector[3],0E0)) }; (:~ : pw() : Accessor for the raw precision w coordinate; returns 0 for 2D or 3D vector : : @param $vector: the vector : @return raw w coordinate :) declare function this:pw($vector as xs:double*) as xs:double { head(($vector[4],0E0)) }; (:~ : pxy() : Accessor for the raw precision xy vector. : : @param $vector: the vector : @return new vector of just the raw x and y coordinates :) declare function this:pxy($vector as xs:double*) as xs:double* { head(($vector[1],0E0)), head(($vector[1],0E0)) }; (:~ : pxz() : Accessor for the raw precision xz vector. : : @param $vector: the vector : @return new vector of just the raw x and z coordinates :) declare function this:pxz($vector as xs:double*) as xs:double* { head(($vector[1],0E0)), head(($vector[3],0E0)) }; (:~ : x() : Accessor for the snapped x coordinate : : @param $vector: the vector : @return x coordinate, rounded :) declare function this:x($vector as xs:double*) as xs:integer { let $x := head(($vector[1],0)) return ( if ($x = xs:double("INF")) then $util:UINT64_MAX else if ($x = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($x) cast as xs:integer ) }; (:~ : y() : Accessor for the snapped y coordinate : : @param $vector: the vector : @return y coordinate, rounded :) declare function this:y($vector as xs:double*) as xs:integer { let $y := head(($vector[2],0)) return ( if ($y = xs:double("INF")) then $util:UINT64_MAX else if ($y = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($y) cast as xs:integer ) }; (:~ : z() : Accessor for the snapped z coordinate : : @param $vector: the vector : @return z coordinate, rounded :) declare function this:z($vector as xs:double*) as xs:integer { let $z := head(($vector[3],0)) return ( if ($z = xs:double("INF")) then $util:UINT64_MAX else if ($z = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($z) cast as xs:integer ) }; (:~ : w() : Accessor for the snapped w coordinate : : @param $vector: the vector : @return w coordinate, rounded :) declare function this:w($vector as xs:double*) as xs:integer { let $w := head(($vector[4],0)) return ( if ($w = xs:double("INF")) then $util:UINT64_MAX else if ($w = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($w) cast as xs:integer ) }; (:~ : magnitude() : Magnitude of the vector from origin. : : @param $v: the vector : @return Euclidean length of vector :) declare function this:magnitude($v as xs:double*) as xs:double { math:sqrt( fn:sum(for $c in $v return $c*$c) ) }; (:~ : dot() : Compute the dot product of two vectors : : @param $p1: one vector : @param $p2: another vector : @return p1·p2 :) declare function this:dot($p1 as xs:double*, $p2 as xs:double*) as xs:double { fn:sum( this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a * $b}, $p1, $p2) ) }; (:~ : dot2() : Compute the dot product of a vector with itself : : @param $p: the vector : @return p·p :) declare function this:dot2($p1 as xs:double*) as xs:double { fn:sum( this:map(function ($a as xs:double) as xs:double {$a * $a}, $p1) ) }; (:~ : Negated dot: useful for some of the SDF functions. : : @param $p1: one vector : @param $p2: another vector : @return p1·-p2 :) declare function this:ndot($p1 as xs:double*, $p2 as xs:double*) as xs:double { fn:sum( this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a * -$b}, $p1, $p2) ) }; (:~ : Linear combination of two vectors, coordinate by coordinate. : Generally $f in in [0,1], but you can : get extrapolations with numbers outside that range. : : @param $u: one vector : @param $v: other vector : @param $f: combination fraction : @return vector of linear combination of each coordinate pair :) declare function this:mix( $u as xs:double*, $v as xs:double*, $f as xs:double ) as xs:double* { (: Logically $a + ($b - $a) * $f but better endvector behaviour :) this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a * (1 - $f) + $b * $f}, $u, $v) }; (:~ : determinant() : Compute the determinant of two 2D vectors. : : @param $u: one vector : @param $v: another vector : @param det(u,v) :) declare function this:determinant($u as xs:double*, $v as xs:double*) as xs:double { this:px($u)*this:py($v) - this:py($u)*this:px($v) }; (:~ : cross() : Compute the cross product of two 3D vectors. : : @param $u: one vector : @param $v: another vector : @return uXv :) declare function this:cross($u as xs:double*, $v as xs:double*) as xs:double* { this:py($u)*this:pz($v) - this:pz($u)*this:py($v), this:pz($u)*this:px($v) - this:px($u)*this:pz($v), this:px($u)*this:py($v) - this:py($u)*this:px($v) }; (:~ : round() : Round the coordinates of the vector. : : @param $v: vector : @return rounded vector :) declare function this:round($v as xs:double*) as xs:double* { this:map(function ($c as xs:double) as xs:double {fn:round($c)}, $v) }; (:~ : times() : Scale every coordinate by a constant, returning the new vector. : : @param $u: vector to scale : @param $k: constant multiplier : @return scaled vector :) declare function this:times($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($c as xs:double) as xs:double {$k * $c}, $v) }; (:~ : multiply() : Coordinate-by-coordinate multiplication. : : @param $u: one vector : @param $v: another vector : @return new vector :) declare function this:multiply($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a * $b}, $u, $v) }; (:~ : div() : Divide every coordinate by a constant, returning the new vector. : : @param $u: vector to scale : @param $k: constant multiplier : @return scaled vector :) declare function this:div($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($c as xs:double) as xs:double {($c div $k) cast as xs:double}, $v) }; (:~ : divide() : Coordinate-by-coordinate division : : @param $u: one vector : @param $v: another vector : @return new vector :) declare function this:divide($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a div $b}, $u, $v) }; (:~ : mod() : Compute the mod of every coordinate by a constant, returning the new vector. : : @param $u: vector : @param $k: constant modulus : @return new vector :) declare function this:mod($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($c as xs:double) as xs:double {$c mod $k}, $v) }; (:~ : divide() : Coordinate-by-coordinate modulo. : : @param $u: one vector : @param $v: another vector : @return new vector :) declare function this:modulo($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a mod $b}, $u, $v) }; (:~ : add() : Add two vectors, returning the new vector. : : @param $u: one vector : @param $v: the other vector : @return u+v :) declare function this:add($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a + $b}, $u, $v) }; (:~ : plus() : Add a constant to every coordinate in a vector, returning the new vector. : : @param $v: the vector : @param $k: constant to add : @return new vector :) declare function this:plus($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {$a + $k}, $v) }; (:~ : minus() : Negate the coordinates of the vector, returning the new vector. : : @param $v: the vector : @return -v :) declare function this:minus($v as xs:double*) as xs:double* { this:map(function ($a as xs:double) as xs:double {-$a}, $v) }; (:~ : minus() : Subtract a constant to every coordinate in a vector, returning the new vector. : : @param $v: the vector : @param $k: constant to subtract : @return new vector :) declare function this:minus($v as xs:double*, $k as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {$a - $k}, $v) }; (:~ : sub() : Subtract one vector from another, returning the new vector. : : @param $u: one vector : @param $v: the other vector : @return u-v :) declare function this:sub($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {$a - $b}, $u, $v) }; (:~ : abs() : Coordinate-by-coordinate absolute value. : : @param $v: vector : @return new vector :) declare function this:abs($v as xs:double*) as xs:double* { this:map(function ($a as xs:double) as xs:double {fn:abs($a)}, $v) }; (:~ : sign() : Coordinate-by-coordinate sign. : : @param $v: vector : @return new vector :) declare function this:sign($v as xs:double*) as xs:double* { this:map( function ($a as xs:double) as xs:double { if ($a = 0) then 0E0 else if ($a < 0) then -1E0 else 1E0 }, $v ) }; (:~ : min() : Coordinate-by-coordinate minimum: minimum of each coordinate pair. : : @param $u: one vector : @param $v: another vector : @return new vector :) declare function this:min($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {fn:min(($a,$b))}, $u, $v) }; (:~ : max() : Coordinate-by-coordinate maximum: maximum of each coordinate pair. : : @param $u: one vector : @param $v: another vector : @return new vector :) declare function this:max($u as xs:double*, $v as xs:double*) as xs:double* { this:map2(function ($a as xs:double, $b as xs:double) as xs:double {fn:max(($a,$b))}, $u, $v) }; (:~ : at-min() : Coordinate-by-coordinate minimum: minimum of each coordinate with value. : : @param $v: vector : @param $val: comparison value : @return new vector :) declare function this:at-min($v as xs:double*, $val as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {fn:min(($a, $val))}, $v) }; (:~ : at-max() : Coordinate-by-coordinate maximum: maximum of each coordinate with value. : : @param $v: vector : @param $val: comparison value : @return new vector :) declare function this:at-max($v as xs:double*, $val as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {fn:max(($a, $val))}, $v) }; (:~ : normalize() : Normalize a vector represented as a vector (scale to unit vector), returning : the new vector. : : @param $v: the vector : @return normalized vector :) declare function this:normalize($v as xs:double*) as xs:double* { let $l := this:magnitude($v) return if ($l = 0) then $v else this:map(function ($c as xs:double) as xs:double {$c div $l}, $v) }; (:~ : clamp() : Coordinate-by-coordinate clamping. : : @param $v: vector : @param $min: minimum value : @param $max: maximum value : @return new vector :) declare function this:clamp($v as xs:double*, $min as xs:double, $max as xs:double) as xs:double* { this:map(function ($a as xs:double) as xs:double {util:clamp($a, $min, $max)}, $v) }; (:~ : clampv() : Coordinate-by-coordinate clamping: clamp with corresponding coordinates of : min/max vectors. : : @param $v: vector : @param $minv: minimum values : @param $maxv: maximum values : @return new vector :) declare function this:clampv($v as xs:double*, $minv as xs:double*, $maxv as xs:double*) as xs:double* { this:map3(function ($a as xs:double, $min as xs:double?, $max as xs:double?) as xs:double {util:clamp($a, $min, $max)}, $v, $minv, $maxv) }; (:~ : perpendicular() : Compute the perpendicular vector for a 2D vector represented as a vector. : (Rotated by 90 degrees.) : : @param $v: the vector : @return perpendicular vector :) declare function this:perpendicular($v as xs:double*) as xs:double* { (this:py($v), -this:px($v)) }; (:~ : snap() : Snap the coordinates of the vector, returning the vector with snapped : (i.e. integer) coordinates. NaN snaps to UINT64_MAX. : : @param $vector: the vector :) declare function this:snap($vector as xs:double*) as xs:integer* { for-each($vector, function ($c as xs:double) as xs:integer { if ($c = xs:double("INF") or not($c = $c)) then $util:UINT64_MAX else if ($c = xs:double("-INF")) then -$util:UINT64_MAX else round-half-to-even($c) cast as xs:integer } ) }; (:~ : floor() : Return vector where all the coordinates have been set to the floor of : the original vector's values. Differs from snap() for negative coordinates. : : @param $vector: the vector :) declare function this:floor($vector as xs:double*) as xs:integer* { for-each($vector, function ($c as xs:double) as xs:integer {fn:floor($c) cast as xs:integer}) }; (:~ : decimal() : Perform decimal rounding on all the coordinates (see util:decimal). : : @param $vector: the vector to round : @param $digits: how many digits after the decimal vector to keep :) declare function this:decimal($vector as xs:double*, $digits as xs:integer) as xs:double* { for-each($vector, function ($c as xs:double) as xs:double {util:decimal($c, $digits)}) }; (:~ : quote() : Return a string value for the vector, suitable for debugging. : : @param $vector: the vector to quote :) declare function this:quote($vector as xs:double*) as xs:string { string-join($vector!string(.),",") }; (:~ : valid() : False if a coordinate is INF or NaN :) declare function this:valid($point as xs:double*) as xs:boolean { every $c in $point satisfies $c=$c and not($c=(xs:double("INF"),xs:double("-INF"))) }; (:~ : same() : Equality comparison for vectors, ignoring annotation properties. : Return true() if they have equal coordinates. : : @param $this: one vector : @param $other: the vector to compare it to : @return this=other :) declare function this:same($this as xs:double*, $other as xs:double*) as xs:boolean { let $d := max((count($this), count($other))) return ( deep-equal( this:as-dimension($this, $d), this:as-dimension($other, $d) ) ) }; (:~ : distance() : Euclidean distance between vectors : : @param $v1: one vector : @param $v2: another vector : @return distance(v1,v2) :) declare function this:distance($v1 as xs:double*, $v2 as xs:double*) as xs:double { let $d := fn:max((this:dimension($v1), this:dimension($v2))) return ( math:sqrt( fn:sum( util:zip( function ($c1 as xs:double, $c2 as xs:double) as xs:double { ($c1 - $c2)*($c1 - $c2) }, this:as-dimension($v1, $d), this:as-dimension($v2, $d) ) ) ) ) }; (:~ : polar-distance() : Angular distance between points taken as vectors. :) declare function this:polar-distance( $p as xs:double*, $q as xs:double* ) as xs:double { abs(this:angle((0,0), $p) - this:angle((0,0), $q)) }; (:~ : taxi-distance() : Taxi-cab distance between two points. That is, right angle distance, such as : a taxi going 2 blocks North plus 3 blocks East = distance 5 blocks. :) declare function this:taxi-distance( $p as xs:double*, $q as xs:double* ) as xs:double { let $d := max((count($p), count($q))) return ( sum( util:zip( function ($cp as xs:double, $cq as xs:double) as xs:double { abs($cp - $cq) }, this:as-dimension($p, $d), this:as-dimension($q, $d) ) ) ) }; (:~ : avg-distance() : Average of distances along each dimension. :) declare function this:avg-distance( $p as xs:double*, $q as xs:double* ) as xs:double { let $d := max((count($p), count($q))) return ( avg( util:zip( function ($cp as xs:double, $cq as xs:double) as xs:double { abs($cp - $cq) }, this:as-dimension($p, $d), this:as-dimension($q, $d) ) ) ) }; (:~ : rail-distance() : Railway distance, e.g distance to the central hub and back out. : The hub is the origin. :) declare function this:rail-distance( $p as xs:double*, $q as xs:double* ) as xs:double { this:distance($p, (0,0)) + this:distance($q, (0,0)) }; (:~ : rail-distance() : Railway distance, e.g distance to the central hub and back out where the hub : is specified. :) declare function this:rail-distance( $p as xs:double*, $q as xs:double*, $origin as xs:double* ) as xs:double { this:distance($p, $origin) + this:distance($q, $origin) }; (:~ : chebyshev-distance() : Chebyshev distance metric. The maximum of the dimensional distances. :) declare function this:chebyshev-distance( $p as xs:double*, $q as xs:double* ) as xs:double { let $d := max((count($p), count($q))) return ( max( util:zip( function ($cp as xs:double, $cq as xs:double) as xs:double { abs($cp - $cq) }, this:as-dimension($p, $d), this:as-dimension($q, $d) ) ) ) }; (:~ : minwokski-distance() : Minkowski distance metric of the given order. Certain orders give rise to : other metrics: : order=1=>taxi; order=2=>Euclidean; as order=>∞=>Chebyshev; : order<1 is not a metric unless undo the final pow. i.e. X is a metric but : X^(1/order) is not. :) declare function this:minkowski-distance( $p as xs:double*, $q as xs:double*, $order as xs:double ) as xs:double { let $d := max((count($p), count($q))) return ( math:pow( sum( util:zip( function ($cp as xs:double, $cq as xs:double) as xs:double { math:pow(abs($cp - $cq), $order) }, this:as-dimension($p, $d), this:as-dimension($q, $d) ) ), 1 div $order ) ) }; (:~ : cosine-similarity() : Cosine similarity: (A·B)/(∥A∥∥B∥) = ΣAiBi/(√Ai²√Bi²) :) declare function this:cosine-similarity( $p as xs:double*, $q as xs:double* ) as xs:double { this:dot($p, $q) div (this:magnitude($p)*this:magnitude($q)) }; (:~ : cosine-distance() : Cosine similarity as a metric: acos(similarity)/π => [0,1] :) declare function this:cosine-distance( $p as xs:double*, $q as xs:double* ) as xs:double { math:acos(this:cosine-similarity($p, $q)) div math:pi() }; (:~ : angle() : Compute the angle (azimuth) from one point to the next, in degrees : Return 0 if points are the same : : @param $last: previous point; use (0,0) if no previous : @param $curr: current point :) declare function this:angle($last as xs:double*, $curr as xs:double*) as xs:double { let $this := $curr=>this:as-dimension(2) let $prev := if (empty($last)) then (0,0) else this:as-dimension($last,2) return if (this:same($prev, $this) or (this:distance($prev,$this) < $config:ε)) then 0 else if (this:px($this)=this:px($prev)) then ( if (this:py($prev) > this:py($this)) then 270 (: remapped -90 :) else 90 ) else ( util:remap-degrees(util:degrees( (math:pi() div 2) - math:atan2(this:px($this) - this:px($prev), this:py($this) - this:py($prev)) )) ) }; (:~ : inclination() : Compute the inclination angle from one point in the next, in degrees : Return 90 if the points are the same : : @param $last: previous point; use (0,0,0) if no previous : @param $curr: current point :) declare function this:inclination($last as xs:double*, $curr as xs:double*) as xs:double { let $curr := $curr=>this:as-dimension(3) let $prev := if (empty($last)) then (0,0,0) else $last=>this:as-dimension(3) let $d := this:distance($prev,$curr) return if (this:same($prev,$curr) or ($d < $config:ε)) then 90 else ( util:remap-degrees(util:degrees( math:acos( (this:pz($curr) - this:pz($prev)) div $d ) )) ) }; (:~ : destination() : Point at a particular distance and direction. : : @param $point: starting point : @param $degrees: bearing from point : @param $length: how far along bearing to travel :) declare function this:destination( $point as xs:double*, $degrees as xs:double, $length as xs:double ) as xs:double* { let $angle := util:radians($degrees) return ( this:px($point) + math:cos($angle)*$length, this:py($point) + math:sin($angle)*$length ) }; (:~ : mutate() : Mutate a sequence of 2D vectors using a mutation function. : : @param $vectors: the vectors : @param $mutate: function that takes a vector as an argument and returns a new vector :) declare function this:mutate( $vectors as xs:double*, $mutate as function(xs:double*) as xs:double* ) as xs:double* { for $i in 1 to count($vectors) idiv 2 return ( $mutate(($vectors[2*$i - 1], $vectors[2*$i])) ) };