http://mathling.com/image/matrix library module
http://mathling.com/image/matrix
2D array of colours similar functionality to point matrix, but with
sequences of colours rather than a map. Quicker for creation if you
don't need the sparseness.
Copyright© Mary Holstege 2021-2023
CC-BY (https://creativecommons.org/licenses/by/4.0/)
Status: Stable
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"http://mathling.com/geometric/point
import module namespace point="http://mathling.com/geometric/point" at "../geo/point.xqy"
Variables
Variable: $RESERVED as
Functions
Function: array
declare function array($rows as xs:integer,
$columns as xs:integer,
$data as map(xs:string,item()*)*) as map(*)
declare function array($rows as xs:integer, $columns as xs:integer, $data as map(xs:string,item()*)*) as map(*)
Params
- rows as xs:integer
- columns as xs:integer
- data as map(xs:string,item()*)*
Returns
- map(*)
declare function this:array( $rows as xs:integer, $columns as xs:integer, $data as map(xs:string,item()*)* ) as map(*) { if ($rows * $columns ne count($data)) then errors:error("UTIL-BADARRAY", ($rows, $columns, count($data))) else ( map { "kind": "imagearray", "rows": $rows, "columns": $columns, "data": $data } ) }
Function: kind
declare function kind($array as map(*)) as xs:string
declare function kind($array as map(*)) as xs:string
Params
- array as map(*)
Returns
- xs:string
declare function this:kind($array as map(*)) as xs:string { $array("kind") }
Function: rows
declare function rows($array as map(*)) as xs:integer
declare function rows($array as map(*)) as xs:integer
Params
- array as map(*)
Returns
- xs:integer
declare function this:rows($array as map(*)) as xs:integer { $array("rows") }
Function: columns
declare function columns($array as map(*)) as xs:integer
declare function columns($array as map(*)) as xs:integer
Params
- array as map(*)
Returns
- xs:integer
declare function this:columns($array as map(*)) as xs:integer { $array("columns") }
Function: data
declare function data($array as map(*)) as map(xs:string,item()*)*
declare function data($array as map(*)) as map(xs:string,item()*)*
Params
- array as map(*)
Returns
- map(xs:string,item()*)*
declare function this:data($array as map(*)) as map(xs:string,item()*)* { $array("data") }
Function: get
declare function get($matrix as map(*),
$row as xs:integer,
$column as xs:integer) as map(xs:string,item()*)
declare function get($matrix as map(*), $row as xs:integer, $column as xs:integer) as map(xs:string,item()*)
Params
- matrix as map(*)
- row as xs:integer
- column as xs:integer
Returns
- map(xs:string,item()*)
declare function this:get( $matrix as map(*), $row as xs:integer, $column as xs:integer ) as map(xs:string,item()*) { this:data($matrix)[($row - 1)*this:columns($matrix) + $column] }
Function: get
declare function get($matrix as map(*),
$point as map(xs:string,item()*)) as map(xs:string,item()*)
declare function get($matrix as map(*), $point as map(xs:string,item()*)) as map(xs:string,item()*)
Params
- matrix as map(*)
- point as map(xs:string,item()*)
Returns
- map(xs:string,item()*)
declare function this:get( $matrix as map(*), $point as map(xs:string,item()*) ) as map(xs:string,item()*) { this:data($matrix)[(point:y($point) - 1)*this:columns($matrix) + point:x($point)] }
Function: row
declare function row($matrix as map(*),
$row as xs:integer) as map(xs:string,item()*)*
declare function row($matrix as map(*), $row as xs:integer) as map(xs:string,item()*)*
row()
Return all the values from the given row
Params
- matrix as map(*): the matrix
- row as xs:integer: the row number
Returns
- map(xs:string,item()*)*
declare function this:row( $matrix as map(*), $row as xs:integer ) as map(xs:string,item()*)* { if ($row < 1 or $row gt $matrix("rows")) then () else let $data := $matrix("data") let $num-columns := $matrix("columns") let $ixs := for $col in 1 to $num-columns return ($row - 1)*$num-columns + $col (: Following works because indexes unique and in order :) return $data[position()=$ixs] }
Function: column
declare function column($matrix as map(*),
$column as xs:integer) as map(xs:string,item()*)*
declare function column($matrix as map(*), $column as xs:integer) as map(xs:string,item()*)*
column()
Return all the values from the given column
Params
- matrix as map(*): the matrix
- column as xs:integer: the column number
Returns
- map(xs:string,item()*)*
declare function this:column( $matrix as map(*), $column as xs:integer ) as map(xs:string,item()*)* { if ($column lt 1 or $column gt $matrix("columns")) then () else let $data := $matrix("data") let $num-rows := $matrix("rows") let $num-columns := $matrix("columns") let $ixs := for $row in 1 to $num-rows return ($row - 1)*$num-columns + $column (: Following works because indexes unique and in order :) return $data[position()=$ixs] }
Function: diagonal
declare function diagonal($matrix as map(*)) as map(xs:string,item()*)*
declare function diagonal($matrix as map(*)) as map(xs:string,item()*)*
diagonal()
Return all the values from the diagonal
Raises an error if the matrix is not square
Params
- matrix as map(*): the matrix
Returns
- map(xs:string,item()*)*
declare function this:diagonal($matrix as map(*)) as map(xs:string,item()*)* { let $data := $matrix("data") let $num-rows := $matrix("rows") let $num-columns := $matrix("columns") return ( if ($num-rows != $num-columns) then ( errors:error("UTIL-MATRIXNOTSQUARE", ($num-rows,$num-columns)) ) else ( let $ixs := for $i in 1 to $num-rows return ($i - 1)*$num-columns + $i (: Following works because indexes unique and in order :) return $data[position()=$ixs] ) ) }
Function: sample
declare function sample($matrix as map(*),
$row as xs:integer,
$column as xs:integer,
$sample-size as xs:integer) as map(xs:string,item()*)*
declare function sample($matrix as map(*), $row as xs:integer, $column as xs:integer, $sample-size as xs:integer) as map(xs:string,item()*)*
sample()
Return all the values in a sample of the matrix from row,column and
extending for sample-size in each direction (if possible). Pad missing
data with data from last row or column, repeated.
Params
- matrix as map(*): the matrix
- row as xs:integer: starting row
- column as xs:integer: starting column
- sample-size as xs:integer: extent of sample
Returns
- map(xs:string,item()*)*
declare function this:sample( $matrix as map(*), $row as xs:integer, $column as xs:integer, $sample-size as xs:integer ) as map(xs:string,item()*)* { if ($row lt 1 or $row gt $matrix("rows")) then (util:log("bad row "||$row)) else if ($column lt 1 or $column gt $matrix("columns")) then (util:log("bad column "||$column)) else let $data := $matrix("data") let $num-rows := $matrix("rows") let $num-columns := $matrix("columns") return ( (: If we don't have to pad, we can use a faster approach :) if ($row + $sample-size - 1 <= $num-rows and $column + $sample-size - 1 <= $num-columns) then ( let $ixs := ( for $r in $row to $row + $sample-size - 1 for $c in $column to $column + $sample-size - 1 return ( ($r - 1)*$num-columns + $c ) ) return ( (: Following works because indexes unique and in order :) $data[position()=$ixs] ) ) else ( let $ixs := ( for $r in $row to $row + $sample-size - 1 for $c in $column to $column + $sample-size - 1 let $r := if ($r > $num-rows) then $num-rows else $r let $c := if ($c > $num-columns) then $num-columns else $c return ( ($r - 1)*$num-columns + $c ) ) return ( (: We may have duplicate/out of order indices: have to iterate :) for-each($ixs, function($ix as xs:integer) as map(xs:string,item()*)? { $data[position()=$ix] }) ) ) ) }
Function: data
declare function data($array as map(*), $data as map(xs:string,item()*)*) as map(*)
declare function data($array as map(*), $data as map(xs:string,item()*)*) as map(*)
data()
Replace the data.
Params
- array as map(*)
- data as map(xs:string,item()*)*
Returns
- map(*)
declare function this:data($array as map(*), $data as map(xs:string,item()*)*) as map(*) { if (this:rows($array) * this:columns($array) ne count($data)) then errors:error("UTIL-BADARRAY", (this:rows($array), this:columns($array), count($data))) else ( $array=>map:put("data", $data) ) }
Original Source Code
xquery version "3.1"; (:~ : 2D array of colours similar functionality to point matrix, but with : sequences of colours rather than a map. Quicker for creation if you : don't need the sparseness. : : Copyright© Mary Holstege 2021-2023 : CC-BY (https://creativecommons.org/licenses/by/4.0/) : @since December 2021 : @custom:Status Stable :) module namespace this="http://mathling.com/image/matrix"; 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"; import module namespace point="http://mathling.com/geometric/point" at "../geo/point.xqy"; declare namespace map="http://www.w3.org/2005/xpath-functions/map"; declare namespace math="http://www.w3.org/2005/xpath-functions/math"; declare variable $this:RESERVED := ("kind","rows","columns","data"); declare function this:array( $rows as xs:integer, $columns as xs:integer, $data as map(xs:string,item()*)* ) as map(*) { if ($rows * $columns ne count($data)) then errors:error("UTIL-BADARRAY", ($rows, $columns, count($data))) else ( map { "kind": "imagearray", "rows": $rows, "columns": $columns, "data": $data } ) }; declare function this:kind($array as map(*)) as xs:string { $array("kind") }; declare function this:rows($array as map(*)) as xs:integer { $array("rows") }; declare function this:columns($array as map(*)) as xs:integer { $array("columns") }; declare function this:data($array as map(*)) as map(xs:string,item()*)* { $array("data") }; declare function this:get( $matrix as map(*), $row as xs:integer, $column as xs:integer ) as map(xs:string,item()*) { this:data($matrix)[($row - 1)*this:columns($matrix) + $column] }; declare function this:get( $matrix as map(*), $point as map(xs:string,item()*) ) as map(xs:string,item()*) { this:data($matrix)[(point:y($point) - 1)*this:columns($matrix) + point:x($point)] }; (:~ : row() : Return all the values from the given row : : @param $matrix: the matrix : @param $row: the row number :) declare function this:row( $matrix as map(*), $row as xs:integer ) as map(xs:string,item()*)* { if ($row < 1 or $row gt $matrix("rows")) then () else let $data := $matrix("data") let $num-columns := $matrix("columns") let $ixs := for $col in 1 to $num-columns return ($row - 1)*$num-columns + $col (: Following works because indexes unique and in order :) return $data[position()=$ixs] }; (:~ : column() : Return all the values from the given column : : @param $matrix: the matrix : @param $column: the column number :) declare function this:column( $matrix as map(*), $column as xs:integer ) as map(xs:string,item()*)* { if ($column lt 1 or $column gt $matrix("columns")) then () else let $data := $matrix("data") let $num-rows := $matrix("rows") let $num-columns := $matrix("columns") let $ixs := for $row in 1 to $num-rows return ($row - 1)*$num-columns + $column (: Following works because indexes unique and in order :) return $data[position()=$ixs] }; (:~ : diagonal() : Return all the values from the diagonal : Raises an error if the matrix is not square : : @param $matrix: the matrix :) declare function this:diagonal($matrix as map(*)) as map(xs:string,item()*)* { let $data := $matrix("data") let $num-rows := $matrix("rows") let $num-columns := $matrix("columns") return ( if ($num-rows != $num-columns) then ( errors:error("UTIL-MATRIXNOTSQUARE", ($num-rows,$num-columns)) ) else ( let $ixs := for $i in 1 to $num-rows return ($i - 1)*$num-columns + $i (: Following works because indexes unique and in order :) return $data[position()=$ixs] ) ) }; (:~ : sample() : Return all the values in a sample of the matrix from row,column and : extending for sample-size in each direction (if possible). Pad missing : data with data from last row or column, repeated. : : @param $matrix: the matrix : @param $row: starting row : @param $column: starting column : @param $sample-size: extent of sample :) declare function this:sample( $matrix as map(*), $row as xs:integer, $column as xs:integer, $sample-size as xs:integer ) as map(xs:string,item()*)* { if ($row lt 1 or $row gt $matrix("rows")) then (util:log("bad row "||$row)) else if ($column lt 1 or $column gt $matrix("columns")) then (util:log("bad column "||$column)) else let $data := $matrix("data") let $num-rows := $matrix("rows") let $num-columns := $matrix("columns") return ( (: If we don't have to pad, we can use a faster approach :) if ($row + $sample-size - 1 <= $num-rows and $column + $sample-size - 1 <= $num-columns) then ( let $ixs := ( for $r in $row to $row + $sample-size - 1 for $c in $column to $column + $sample-size - 1 return ( ($r - 1)*$num-columns + $c ) ) return ( (: Following works because indexes unique and in order :) $data[position()=$ixs] ) ) else ( let $ixs := ( for $r in $row to $row + $sample-size - 1 for $c in $column to $column + $sample-size - 1 let $r := if ($r > $num-rows) then $num-rows else $r let $c := if ($c > $num-columns) then $num-columns else $c return ( ($r - 1)*$num-columns + $c ) ) return ( (: We may have duplicate/out of order indices: have to iterate :) for-each($ixs, function($ix as xs:integer) as map(xs:string,item()*)? { $data[position()=$ix] }) ) ) ) }; (:~ : data() : Replace the data. :) declare function this:data($array as map(*), $data as map(xs:string,item()*)*) as map(*) { if (this:rows($array) * this:columns($array) ne count($data)) then errors:error("UTIL-BADARRAY", (this:rows($array), this:columns($array), count($data))) else ( $array=>map:put("data", $data) ) };