http://mathling.com/svg/gradients library module
http://mathling.com/svg/gradients
Module with functions providing some colouring support at the
algorithm level.
Copyright© Mary Holstege 2020-2023
CC-BY (https://creativecommons.org/licenses/by/4.0/)
Status: Active
Imports
http://mathling.com/core/randomimport module namespace rand="http://mathling.com/core/random" at "../core/random.xqy"http://mathling.com/colour/space
import module namespace cs="http://mathling.com/colour/space" at "../colourspace/colour-space.xqy"http://mathling.com/geometric/path
import module namespace path="http://mathling.com/geometric/path" at "../geo/path.xqy"http://mathling.com/geometric/edge
import module namespace edge="http://mathling.com/geometric/edge" at "../geo/edge.xqy"http://mathling.com/core/utilities
import module namespace util="http://mathling.com/core/utilities" at "../core/utilities.xqy"http://mathling.com/colour/tonemap
import module namespace tone="http://mathling.com/colour/tonemap" at "../colourspace/tonemap.xqy"http://mathling.com/colour/stop
import module namespace stop="http://mathling.com/colour/stop" at "../colourspace/stop.xqy"http://mathling.com/colour/xyz
import module namespace xyz="http://mathling.com/colour/xyz" at "../colourspace/xyz.xqy"http://mathling.com/geometric/ellipse
import module namespace ellipse="http://mathling.com/geometric/ellipse" at "../geo/ellipse.xqy"http://mathling.com/geometric/spline
import module namespace spline="http://mathling.com/geometric/spline" at "../geo/spline.xqy"http://mathling.com/colour/rgb
import module namespace rgb="http://mathling.com/colour/rgb" at "../colourspace/rgb.xqy"http://mathling.com/geometric/point
import module namespace point="http://mathling.com/geometric/point" at "../geo/point.xqy"
Variables
Variable: $NAMED-GRADIENTS as xs:string*
Variable: $ALIASES as map(xs:string,xs:string)
Variable: $NAMED-ALIASES as xs:string*
Variable: $WHITES as xs:string*
Variable: $PRETTY-PALETTES as map(xs:string,xs:string*)
PrettyCols from https://nrennie.github.io/PrettyCols/ CC0
Variable: $WERNER-PALETTES as map(xs:string,xs:string*)
Werner colours from Werner's Nomenclature of Colours
Variable: $PALETTES as map(xs:string,xs:string*)
Variable: $NAMED-PALETTES as xs:string*
Variable: $SMALL-GRADIENTS as xs:string*
Variable: $SPLIT-GRADIENTS as xs:string*
Variable: $CYCLIC-GRADIENTS as xs:string*
Variable: $DIVERGING-GRADIENTS as xs:string*
Variable: $LINEAR-GRADIENTS as xs:string*
Functions
Function: starts-darker
declare function starts-darker($gradient as xs:string) as xs:boolean
declare function starts-darker($gradient as xs:string) as xs:boolean
starts-darker()
Does this gradient start with darker colours? i.e. dark-to-light
Return false for those that are dark-light-dark or light-dark-light or
scrambled
Params
- gradient as xs:string
Returns
- xs:boolean
declare function this:starts-darker($gradient as xs:string) as xs:boolean { ($gradient = ( "acton", "bamako", "batlow", "buda", "davos", "devon", "hawaii", "imola", "lapaz", "nuuk", "oleron", "oslo", "tokyo", "turku", "actonS", "bamakoS", "batlowS", (:"berlinS",:) "bilbaoS", (:"brocS",:) "budaS", "inferno", "magma", "plasma", "viridis", "cividis", "twilight-blue", "twilight-red", "sawtooth","azimuth", "cube1_0","cubeYF","Linear_L", "bathymetry","bathymetryO","CDOM","chlorophyll","density", "freesurface-red","freesurface-blue","oxygen","PAR","salinity", "temperature","turbidity","velocity-blue","velocity-green", "vorticity-pink","vorticity-turquoise", "autumn","blackbody","bluered","bone","cool","copper", "cubehelix","earth","electric","greens","greys","hot", "jet","picnic","portland","rainbow", "summer","warm","winter","yignbu","yiorrd", "expanded-bone","expanded-copper", "CET-L9","CET-L8","CET-L7","CET-L6","CET-L5", "CET-L4","CET-L3","CET-L2","CET-L1","CET-L18", "CET-L16","CET-L15","CET-L14","CET-L13","CET-L11","CET-L10", "CET-CBTL2","CET-CBTL1", "CET-CBL2","CET-CBL1", "bluegold", "burn", "jungle", "sea", "blossom", "oasis", "blood", "forest", "deepsea", "cm-gray", "haline", "ice", "oxy", "solar", "thermal", "topo", "turbid", "simpleBlack", "browns", "olives", "sunsetland", "october", "farhills", "springhills", "mountains", "wotw", "carnival", "greens", "werner_reds", "werner_blues", "werner_browns", "Blues", "Purples", "Tangerines", "Greens", "Pinks", "Teals", "Yellows", "Reds" )) or (contains($gradient,"-reverse") and not(this:starts-darker(replace($gradient,"-reverse",""))) ) }
Function: random-gradient
declare function random-gradient() as xs:string
declare function random-gradient() as xs:string
random-gradient()
Return then name of a random well-known base gradient or its reverse.
Returns
- xs:string
declare function this:random-gradient() as xs:string { let $exclusion := "(^simple)" let $valid-names := ($this:NAMED-GRADIENTS,$this:NAMED-ALIASES,$this:NAMED-PALETTES)[ not(matches(.,$exclusion)) ] let $gradient := rand:select-random($valid-names) return ( if (empty($gradient) or $gradient="") then util:log("Empty gradient? "||string-join($valid-names,"+")) else (), util:assert(not(empty($gradient)), "__empty__"), util:assert($gradient ne "", "__empty string__"), if (rand:flip(50)) then $gradient else $gradient||"-reverse" ) }
Function: random-gradient
declare function random-gradient($exclusion as xs:string) as xs:string
declare function random-gradient($exclusion as xs:string) as xs:string
random-gradient()
Return then name of a random well-known base gradient or its reverse.
Params
- exclusion as xs:string: regular expression of gradient names to exclude in addition to simpleXXX
Returns
- xs:string
declare function this:random-gradient($exclusion as xs:string) as xs:string { let $exclusion := "("||$exclusion||")|(^simple)" let $valid-names := ($this:NAMED-GRADIENTS,$this:NAMED-ALIASES,$this:NAMED-PALETTES)[ not(matches(.,$exclusion)) ] let $gradient := rand:select-random($valid-names) return ( if (rand:flip(50)) then $gradient else $gradient||"-reverse" ) }
Function: fetch-gradient
declare function fetch-gradient($gradient as xs:string) as element()?
declare function fetch-gradient($gradient as xs:string) as element()?
fetch-gradient()
Get the gradient definition of a known named gradient or its alias.
Empty otherwise.
Params
- gradient as xs:string
Returns
- element()?
declare function this:fetch-gradient($gradient as xs:string) as element()? { if ($gradient = $this:NAMED-GRADIENTS) then ( (: SLEAZY HACK ALERT :) let $def := try { doc("../COLOURS/"||$gradient||".xsl")//xsl:param/* } catch * { () } return if (exists($def)) then $def else ( try { doc("../COLOURS/"||$gradient||".svg")//svg:defs/* } catch * { () } ) ) else if ($this:ALIASES($gradient) = $this:NAMED-GRADIENTS) then ( this:fetch-gradient($this:ALIASES($gradient)) ) else ( ) }
Function: get-gradient-definition
declare function get-gradient-definition($gradient as xs:string) as element()?
declare function get-gradient-definition($gradient as xs:string) as element()?
get-gradient-definition()
Get the gradient definition of the name, allowing for variants for
flips, inversions, samples, etc.
Params
- gradient as xs:string
Returns
- element()?
declare function this:get-gradient-definition($gradient as xs:string) as element()? { let $gradient-pieces := tokenize($gradient, "[·+]") (: middle dot :) let $do-interleave := contains($gradient,"+") let $gradients := for $gradient in $gradient-pieces return ( if ($gradient="random") then ( this:get-gradient-definition(this:random-gradient()) ) else if ($gradient = $this:NAMED-GRADIENTS) then ( this:fetch-gradient($gradient) ) else if ($this:ALIASES($gradient) = $this:NAMED-GRADIENTS) then ( let $def := this:fetch-gradient($this:ALIASES($gradient)) return ( <svg:linearGradient id="{$gradient}">{ $def/(@* except @id), $def/* }</svg:linearGradient> ) ) else if ($gradient = $this:NAMED-PALETTES) then ( this:linear-gradient-definition($gradient, "linear", $this:PALETTES($gradient)) ) else if (matches($gradient,'(-reverse|-random|-flip|-invert|-full|-anti|-outflow|-inflow|-slant|-fade|-[0-9]+)$')) then ( let $root := replace($gradient,'(-reverse|-random|-flip|-invert|-full|-anti|-outflow|-inflow|-slant|-fade|-[0-9]+)$','') let $definition := this:get-gradient-definition($root) let $operation := substring-after($gradient, $root||"-") return switch ($operation) case "random" return this:gradient-random($definition)=>this:gradient-name($gradient) case "reverse" return this:gradient-reverse($definition)=>this:gradient-name($gradient) case "flip" return this:linear-flip($definition)=>this:gradient-name($gradient) case "invert" return this:linear-invert($definition)=>this:gradient-name($gradient) case "full" return this:linear-full($definition)=>this:gradient-name($gradient) case "anti" return this:linear-anti($definition)=>this:gradient-name($gradient) case "outflow" return this:radial-outflow($definition)=>this:gradient-name($gradient) case "inflow" return this:radial-inflow($definition)=>this:gradient-name($gradient) case "slant" return this:linear-slant($definition)=>this:gradient-name($gradient) case "fade" return this:gradient-fade($definition, 0.0, 1.0)=>this:gradient-name($gradient) default (: one of the samples :) return ( let $n := xs:integer($operation) return this:gradient-sample($definition, $n)=>this:gradient-name($gradient) ) ) else if (starts-with($gradient,"black-to-")) then ( let $colour := this:colour(substring-after($gradient,"black-to-")) return <svg:linearGradient id="{this:safe($gradient)}" gradientUnits="objectBoundingBox" spreadMethod="pad" x1="0%" x2="100%" y1="0%" y2="0%"> <svg:stop offset="0%" stop-color="#000000"/> <svg:stop offset="100%" stop-color="{$colour}"/> </svg:linearGradient> ) else if (starts-with($gradient,"white-to-")) then ( let $colour := this:colour(substring-after($gradient,'white-to-')) return <svg:linearGradient id="{this:safe($gradient)}" gradientUnits="objectBoundingBox" spreadMethod="pad" x1="0%" x2="100%" y1="0%" y2="0%"> <svg:stop offset="0%" stop-color="#FFFFFF"/> <svg:stop offset="100%" stop-color="{$colour}"/> </svg:linearGradient> ) else ( (: util:log("gradient="||$gradient||" no definition") :) ) ) return ( if (count($gradients) le 1) then $gradients else if (count($gradients) = count($gradient-pieces)) then ( this:merge($gradient, $gradients, $do-interleave) ) else ( ) ) }
Function: gradient
declare function gradient($gradient as xs:string) as element()?
declare function gradient($gradient as xs:string) as element()?
Params
- gradient as xs:string
Returns
- element()?
declare function this:gradient($gradient as xs:string) as element()? { this:get-gradient-definition($gradient) }
Function: random-colour
declare function random-colour($gradients as xs:string*) as xs:string
declare function random-colour($gradients as xs:string*) as xs:string
Params
- gradients as xs:string*
Returns
- xs:string
declare function this:random-colour($gradients as xs:string*) as xs:string { rand:select-random(this:colours($gradients)) }
Function: random-colour
declare function random-colour($gradients as xs:string*, $luminance as xs:double) as xs:string
declare function random-colour($gradients as xs:string*, $luminance as xs:double) as xs:string
Params
- gradients as xs:string*
- luminance as xs:double
Returns
- xs:string
declare function this:random-colour($gradients as xs:string*, $luminance as xs:double) as xs:string { rand:select-random(this:colours($gradients))=> rgb:rgb()=>cs:rgb-to-xyz()=> tone:luminate($luminance)=> cs:xyz-to-rgb()=>rgb:to-string() }
Function: random-colour
declare function random-colour($gradients as xs:string*, $low-luminance as xs:double, $high-luminance as xs:double) as xs:string
declare function random-colour($gradients as xs:string*, $low-luminance as xs:double, $high-luminance as xs:double) as xs:string
Params
- gradients as xs:string*
- low-luminance as xs:double
- high-luminance as xs:double
Returns
- xs:string
declare function this:random-colour($gradients as xs:string*, $low-luminance as xs:double, $high-luminance as xs:double) as xs:string { let $colour := rand:select-random(this:colours($gradients)) let $xyz := $colour=>rgb:rgb()=>cs:rgb-to-xyz() let $luminance := tone:luminance($xyz) return ( if (util:twixt($luminance, $low-luminance, $high-luminance)) then $colour else ( $xyz=>tone:luminate(rand:uniform($low-luminance, $high-luminance))=>cs:xyz-to-rgb()=>rgb:to-string() ) ) }
Function: colours
declare function colours($gradients as xs:string*) as xs:string*
declare function colours($gradients as xs:string*) as xs:string*
colours()
Return the colour strings from the named gradients in order.
Params
- gradients as xs:string*
Returns
- xs:string*
declare function this:colours($gradients as xs:string*) as xs:string* { for $gradient in $gradients return ( let $definition := this:get-gradient-definition($gradient) let $colours := if (empty($definition)) then $gradient else $definition/svg:stop/@stop-color return if (matches($gradient,'-reverse')) then ( reverse($colours) ) else ( $colours ) ) }
Function: colours
declare function colours($gradients as xs:string*,
$low as xs:double,
$high as xs:double) as xs:string*
declare function colours($gradients as xs:string*, $low as xs:double, $high as xs:double) as xs:string*
colours()
Return the colour strings from the named gradients in order, but reluminated
to ensure their luminance is in the given range.
Params
- gradients as xs:string*
- low as xs:double
- high as xs:double
Returns
- xs:string*
declare function this:colours( $gradients as xs:string*, $low as xs:double, $high as xs:double ) as xs:string* { let $low := util:clamp-some($low, 0, ()) let $high := util:clamp-some($high, (), 1) let $colours := this:colours($gradients) return ( if ($low <= 0 and $high >= 1) then $colours else ( for $c in $colours let $xyz := rgb:rgb($c)=>cs:rgb-to-xyz() let $luminance := tone:luminance($xyz) return ( if ($luminance < $low) then tone:luminate($xyz, $low)=>cs:xyz-to-rgb()=>rgb:to-string() else if ($luminance > $high) then tone:luminate($xyz, $high)=>cs:xyz-to-rgb()=>rgb:to-string() else $c ) ) ) }
Function: gradient-colours
declare function gradient-colours($gradients as xs:string*) as xs:string*
declare function gradient-colours($gradients as xs:string*) as xs:string*
Params
- gradients as xs:string*
Returns
- xs:string*
declare function this:gradient-colours($gradients as xs:string*) as xs:string* { this:colours($gradients) }
Function: gradient-stops
declare function gradient-stops($gradient as xs:string) as element(svg:stop
)*
declare function gradient-stops($gradient as xs:string) as element(svg:stop )*
gradient-stops()
Return all the gradient stops, as svg:stop elements, from the given gradient.
Params
- gradient as xs:string
Returns
- element(svg:stop)*
declare function this:gradient-stops($gradient as xs:string) as element(svg:stop )* { this:get-gradient-definition($gradient)/svg:stop }
Function: n-gradient-colours
declare function n-gradient-colours($gradient as xs:string) as xs:integer
declare function n-gradient-colours($gradient as xs:string) as xs:integer
Params
- gradient as xs:string
Returns
- xs:integer
declare function this:n-gradient-colours($gradient as xs:string) as xs:integer { let $n := count(this:gradient-stops($gradient)) return if ($n=0) then 1 else $n }
Function: n-colours
declare function n-colours($gradient as xs:string) as xs:integer
declare function n-colours($gradient as xs:string) as xs:integer
Params
- gradient as xs:string
Returns
- xs:integer
declare function this:n-colours($gradient as xs:string) as xs:integer { let $n := count(this:gradient-stops($gradient)) return if ($n=0) then 1 else $n }
Function: gradient-points
declare function gradient-points($gradient as xs:string) as map(xs:string,item()*)*
declare function gradient-points($gradient as xs:string) as map(xs:string,item()*)*
gradient-points()
Return the points in the gradient as gradient points. This assumes
the stop values are in "rgb(r,g,b)" or hex format. Opacity gives the
alpha channel.
A gradient point has an offset and a colour point. The set of
gradient points can be used for interpolations and suchlike.
The colour points returned from this are RGBA points.
TODO: select output colour space
Params
- gradient as xs:string
Returns
- map(xs:string,item()*)*
declare function this:gradient-points($gradient as xs:string) as map(xs:string,item()*)* { for $stop in this:gradient-stops($gradient) let $offset := if (ends-with($stop/@offset,"%")) then xs:double(substring-before($stop/@offset,"%")) div 100 else xs:double($stop/@offset) let $alpha := if (empty($stop/@stop-opacity)) then 1 else if (ends-with($stop/@stop-opacity,"%")) then xs:double(substring-before($stop/@stop-opacity,"%")) div 100 else xs:double($stop/@stop-opacity) let $rgba := rgb:rgba(rgb:rgb($stop/@stop-color), $alpha) return stop:stop("rgb", $offset, $rgba) }
Function: id
declare function id($gradient as element()) as xs:string
declare function id($gradient as element()) as xs:string
Params
- gradient as element()
Returns
- xs:string
declare function this:id($gradient as element()) as xs:string { $gradient/@id }
Function: merge
declare function merge($name as xs:string,
$gradients as element()+,
$do-interleave as xs:boolean) as element()
declare function merge($name as xs:string, $gradients as element()+, $do-interleave as xs:boolean) as element()
Params
- name as xs:string
- gradients as element()+
- do-interleave as xs:boolean
Returns
- element()
declare function this:merge( $name as xs:string, $gradients as element()+, $do-interleave as xs:boolean ) as element() { let $kind := typeswitch(head($gradients)) case element(svg:radialGradient) return "outflow" default return "linear" let $max-n := max(for $gradient in $gradients return count($gradient//svg:stop)) let $stops := if ($do-interleave) then ( for $i in 1 to $max-n for $gradient in $gradients return ( ($gradient//svg:stop)[$i] ) ) else ( for $gradient in $gradients return $gradient//svg:stop ) return ( this:gradient-definition($name, $kind, for $stop in $stops return $stop/@stop-opacity, for $stop in $stops return $stop/@stop-color ) ) }
Function: sample
declare function sample($colours as xs:string*, $num as xs:integer) as xs:string*
declare function sample($colours as xs:string*, $num as xs:integer) as xs:string*
Params
- colours as xs:string*
- num as xs:integer
Returns
- xs:string*
declare function this:sample($colours as xs:string*, $num as xs:integer) as xs:string* { if (empty($colours) or $num <= 1) then () else let $sep := count($colours) div $num for $i in 1 to $num return $colours[1 + ceiling($sep*($i - 1))] }
Function: sample
declare function sample($colours as xs:string*, $above as xs:double, $below as xs:double) as xs:string*
declare function sample($colours as xs:string*, $above as xs:double, $below as xs:double) as xs:string*
Params
- colours as xs:string*
- above as xs:double
- below as xs:double
Returns
- xs:string*
declare function this:sample($colours as xs:string*, $above as xs:double, $below as xs:double) as xs:string* { this:sample($colours, count($colours), $above, $below) }
Function: sample
declare function sample($colours as xs:string*, $num as xs:integer, $above as xs:double, $below as xs:double) as xs:string*
declare function sample($colours as xs:string*, $num as xs:integer, $above as xs:double, $below as xs:double) as xs:string*
Params
- colours as xs:string*
- num as xs:integer
- above as xs:double
- below as xs:double
Returns
- xs:string*
declare function this:sample($colours as xs:string*, $num as xs:integer, $above as xs:double, $below as xs:double) as xs:string* { if (empty($colours) or $num <= 1) then () else let $n := count($colours) let $min-n := round($n * $above) cast as xs:integer let $max-n := round($n * $below) cast as xs:integer (: Asymmetry here because we want to allow 0 for $above and 1 for $below :) let $colours := $colours[position() > $min-n and position() <= $max-n] let $sep := count($colours) div $num for $i in 1 to $num return $colours[1 + ceiling($sep*($i - 1))] }
Function: sample-stops
declare function sample-stops($stops as element(svg:stop)*, $num as xs:integer) as element(svg:stop)*
declare function sample-stops($stops as element(svg:stop)*, $num as xs:integer) as element(svg:stop)*
Params
- stops as element(svg:stop)*
- num as xs:integer
Returns
- element(svg:stop)*
declare function this:sample-stops($stops as element(svg:stop)*, $num as xs:integer) as element(svg:stop)* { if (empty($stops) or $num <= 1) then () else let $sep := count($stops) div $num for $i in 1 to $num return $stops[1 + ceiling($sep*($i - 1))] }
Function: circularize
declare function circularize($colours as xs:string*) as xs:string*
declare function circularize($colours as xs:string*) as xs:string*
Params
- colours as xs:string*
Returns
- xs:string*
declare function this:circularize($colours as xs:string*) as xs:string* { tail($colours), tail(reverse($colours)) }
Function: luminance-range
declare function luminance-range($colour as xs:string,
$n as xs:integer,
$delta as xs:double) as xs:string*
declare function luminance-range($colour as xs:string, $n as xs:integer, $delta as xs:double) as xs:string*
Params
- colour as xs:string
- n as xs:integer
- delta as xs:double
Returns
- xs:string*
declare function this:luminance-range( $colour as xs:string, $n as xs:integer, $delta as xs:double ) as xs:string* { let $rgb := rgb:rgb($colour) let $lum := tone:luminance($rgb) return ( for $l in util:linspace($n, $lum - $delta, $lum, true()) return tone:luminate($rgb, util:clamp($l, 0.0, 1.0))=>rgb:to-string(), $colour, for $l in util:linspace($n, $lum, $lum + $delta)=>tail() return tone:luminate($rgb, util:clamp($l, 0.0, 1.0))=>rgb:to-string() ) }
Function: colour
declare function colour($name as xs:string) as xs:string
declare function colour($name as xs:string) as xs:string
Params
- name as xs:string
Returns
- xs:string
declare function this:colour($name as xs:string) as xs:string { let $known := $rgb:KNOWN-COLOURS($name) return ( if (empty($known)) then $name else $known ) }
Function: ref
declare function ref($name as xs:string) as xs:string
declare function ref($name as xs:string) as xs:string
Params
- name as xs:string
Returns
- xs:string
declare function this:ref($name as xs:string) as xs:string { let $known := $rgb:KNOWN-COLOURS($name) return ( if (exists($known)) then $known else if ($name="none") then $name else if (matches($name, "^(rgb|url|#)")) then $name else "url(#"||this:safe($name)||")" ) }
Function: safe
declare function safe($name as xs:string) as xs:string
declare function safe($name as xs:string) as xs:string
Params
- name as xs:string
Returns
- xs:string
declare function this:safe($name as xs:string) as xs:string { replace(replace($name, "[(),#]", "-"), " ", "") }
Function: average-luminance
declare function average-luminance($colours as xs:string*) as xs:double
declare function average-luminance($colours as xs:string*) as xs:double
average-luminance()
Average luminance: a way of determining relative brightness of a set
of colours.
Params
- colours as xs:string*
Returns
- xs:double
declare function this:average-luminance($colours as xs:string*) as xs:double { avg( for $colour in $colours return ( tone:luminance(cs:rgb-to-xyz(rgb:rgb($colour))) ) ) }
Function: linear-gradient-definition
declare function linear-gradient-definition($name as xs:string,
$layout as xs:integer*, (: x1, x2, y1, y2 :)
$opacities as xs:double*, (: min, max :)
$colours as xs:string*) as element(svg:linearGradient)
declare function linear-gradient-definition($name as xs:string, $layout as xs:integer*, (: x1, x2, y1, y2 :) $opacities as xs:double*, (: min, max :) $colours as xs:string*) as element(svg:linearGradient)
Params
- name as xs:string
- layout as xs:integer*
- opacities as xs:double*
- colours as xs:string*
Returns
- element(svg:linearGradient)
declare function this:linear-gradient-definition( $name as xs:string, $layout as xs:integer*, (: x1, x2, y1, y2 :) $opacities as xs:double*, (: min, max :) $colours as xs:string* ) as element(svg:linearGradient) { let $n := count($colours) let $x1 := ($layout[1],0)[1] let $x2 := ($layout[2],100)[1] let $y1 := ($layout[3],0)[1] let $y2 := ($layout[4],0)[1] let $start-opacity := ($opacities[1],1)[1] let $end-opacity := ($opacities[2],1)[1] return if ($start-opacity=$end-opacity) then ( <svg:linearGradient id="{$name}" gradientUnits="objectBoundingBox" spreadMethod="pad" x1="{$x1}%" x2="{$x2}%" y1="{$y1}%" y2="{$y2}%"> { <svg:stop offset="0.00%" stop-color="{$colours[1]}" stop-opacity="{$start-opacity}"/> , if ($n > 100) then ( for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) (: let $_ := xdmp:log($i||" "||$colour||" "||$offset) :) return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$start-opacity}"/>, if (exists($colours[$i + 1])) then <svg:stop offset="{$offset}%" stop-color="{$colours[$i + 1]}" stop-opacity="{$start-opacity}"/> else () ) ) else ( for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) where $i > 1 and $i < $n return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$start-opacity}"/> ) ) , <svg:stop offset="100.00%" stop-color="{$colours[last()]}" stop-opacity="{$start-opacity}"/> } </svg:linearGradient> ) else ( <svg:linearGradient id="{$name}" gradientUnits="objectBoundingBox" spreadMethod="pad" x1="{$x1}%" x2="{$x2}%" y1="{$y1}%" y2="{$y2}%"> { <svg:stop offset="0.00%" stop-color="{$colours[1]}" stop-opacity="{$start-opacity}"/> , if ($n > 100) then ( let $delta := ($end-opacity - $start-opacity) div $n for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$opacity}"/>, if (exists($colours[$i + 1])) then <svg:stop offset="{$offset}%" stop-color="{$colours[$i + 1]}" stop-opacity="{$opacity}"/> else () ) ) else ( let $delta := ($end-opacity - $start-opacity) div $n for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) where $i > 1 and $i < $n return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$opacity}"/> ) ) , <svg:stop offset="100.00%" stop-color="{$colours[last()]}" stop-opacity="{$end-opacity}"/> } </svg:linearGradient> ) }
Function: linear-standard-layout
declare function linear-standard-layout($kind as xs:string) as xs:integer*
declare function linear-standard-layout($kind as xs:string) as xs:integer*
Params
- kind as xs:string
Returns
- xs:integer*
declare function this:linear-standard-layout($kind as xs:string) as xs:integer* { switch ($kind) case "reverse" return (100,0,0,0) case "flip" return (0,0,0,100) case "invert" return (0,0,100,0) case "full" return (0,100,0,100) case "anti" return (100,0,100,0) case "slant" return (0,50,0,90) default return (0,100,0,0) }
Function: linear-gradient-definition
declare function linear-gradient-definition($name as xs:string,
$kind as xs:string,
$colours as xs:string*) as element(svg:linearGradient)
declare function linear-gradient-definition($name as xs:string, $kind as xs:string, $colours as xs:string*) as element(svg:linearGradient)
Params
- name as xs:string
- kind as xs:string
- colours as xs:string*
Returns
- element(svg:linearGradient)
declare function this:linear-gradient-definition( $name as xs:string, $kind as xs:string, $colours as xs:string* ) as element(svg:linearGradient) { this:linear-gradient-definition($name, this:linear-standard-layout($kind), (1,1), $colours) }
Function: radial-gradient-definition
declare function radial-gradient-definition($name as xs:string,
$layout as xs:integer*, (: r, cx, cy :)
$opacities as xs:double*,
$colours as xs:string*) as element(svg:radialGradient)
declare function radial-gradient-definition($name as xs:string, $layout as xs:integer*, (: r, cx, cy :) $opacities as xs:double*, $colours as xs:string*) as element(svg:radialGradient)
Params
- name as xs:string
- layout as xs:integer*
- opacities as xs:double*
- colours as xs:string*
Returns
- element(svg:radialGradient)
declare function this:radial-gradient-definition( $name as xs:string, $layout as xs:integer*, (: r, cx, cy :) $opacities as xs:double*, $colours as xs:string* ) as element(svg:radialGradient) { let $n := count($colours) let $r := ($layout[1],50)[1] let $cx := ($layout[2],50)[1] let $cy := ($layout[3],$cx)[1] let $start-opacity := ($opacities[1],1)[1] let $end-opacity := ($opacities[2],1)[1] return if ($start-opacity=$end-opacity) then ( <svg:radialGradient id="{$name}" gradientUnits="objectBoundingBox" spreadMethod="pad" cx="{$cx}%" cy="{$cy}%" r="{$r}%"> { <svg:stop offset="0.00%" stop-color="{$colours[1]}" stop-opacity="{$start-opacity}"/> , if ($n > 5) then ( for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$start-opacity}"/>, if ($n > 100 and exists($colours[$i + 1])) then <svg:stop offset="{$offset}%" stop-color="{$colours[$i + 1]}" stop-opacity="{$start-opacity}"/> else () ) ) else ( for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) where $i > 1 and $i < $n return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$start-opacity}"/> ) ) , <svg:stop offset="100.00%" stop-color="{$colours[last()]}" stop-opacity="{$start-opacity}"/> } </svg:radialGradient> ) else ( <svg:radialGradient id="{$name}" gradientUnits="objectBoundingBox" spreadMethod="pad" cx="{$cx}%" cy="{$cy}%" r="{$r}%"> { <svg:stop offset="0.00%" stop-color="{$colours[1]}" stop-opacity="{$start-opacity}"/> , if ($n > 5) then ( let $delta := ($end-opacity - $start-opacity) div $n for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$opacity}"/>, if ($n > 100 and exists($colours[$i + 1])) then <svg:stop offset="{$offset}%" stop-color="{$colours[$i + 1]}" stop-opacity="{$opacity}"/> else () ) ) else ( let $delta := ($end-opacity - $start-opacity) div $n for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) where $i > 1 and $i < $n return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$opacity}"/> ) ) , <svg:stop offset="100.00%" stop-color="{$colours[last()]}" stop-opacity="{$end-opacity}"/> } </svg:radialGradient> ) }
Function: radial-standard-layout
declare function radial-standard-layout($kind as xs:string) as xs:integer*
declare function radial-standard-layout($kind as xs:string) as xs:integer*
Params
- kind as xs:string
Returns
- xs:integer*
declare function this:radial-standard-layout($kind as xs:string) as xs:integer* { if ($kind="stargate") then (50,50,80) else (50,50,50) }
Function: radial-colours
declare function radial-colours($kind as xs:string, $colours as xs:string*) as xs:string*
declare function radial-colours($kind as xs:string, $colours as xs:string*) as xs:string*
Params
- kind as xs:string
- colours as xs:string*
Returns
- xs:string*
declare function this:radial-colours($kind as xs:string, $colours as xs:string*) as xs:string* { if ($kind="inflow") then reverse($colours) else $colours }
Function: radial-gradient-definition
declare function radial-gradient-definition($name as xs:string,
$kind as xs:string,
$colours as xs:string*) as element(svg:radialGradient)
declare function radial-gradient-definition($name as xs:string, $kind as xs:string, $colours as xs:string*) as element(svg:radialGradient)
Params
- name as xs:string
- kind as xs:string
- colours as xs:string*
Returns
- element(svg:radialGradient)
declare function this:radial-gradient-definition( $name as xs:string, $kind as xs:string, $colours as xs:string* ) as element(svg:radialGradient) { this:radial-gradient-definition($name, this:radial-standard-layout($kind), (1,1), this:radial-colours($kind, $colours)) }
Function: gradient-definition
declare function gradient-definition($name as xs:string,
$kind as xs:string,
$opacities as xs:double*,
$colours as xs:string*) as element()
declare function gradient-definition($name as xs:string, $kind as xs:string, $opacities as xs:double*, $colours as xs:string*) as element()
Params
- name as xs:string
- kind as xs:string
- opacities as xs:double*
- colours as xs:string*
Returns
- element()
declare function this:gradient-definition( $name as xs:string, $kind as xs:string, $opacities as xs:double*, $colours as xs:string* ) as element() { if ($kind=("inflow","outflow","stargate")) then ( this:radial-gradient-definition($name, this:radial-standard-layout($kind), $opacities, this:radial-colours($kind, $colours)) ) else ( this:linear-gradient-definition($name, this:linear-standard-layout($kind), $opacities, $colours) ) }
Function: gradient-definition
declare function gradient-definition($name as xs:string,
$kind as xs:string,
$colours as xs:string*) as element()
declare function gradient-definition($name as xs:string, $kind as xs:string, $colours as xs:string*) as element()
Params
- name as xs:string
- kind as xs:string
- colours as xs:string*
Returns
- element()
declare function this:gradient-definition( $name as xs:string, $kind as xs:string, $colours as xs:string* ) as element() { this:gradient-definition($name, $kind, (1,1), $colours) }
Function: is-radial
declare function is-radial($def as element()) as xs:boolean
declare function is-radial($def as element()) as xs:boolean
Params
- def as element()
Returns
- xs:boolean
declare function this:is-radial($def as element()) as xs:boolean { local-name($def)="radialGradient" }
Function: gradient-name
declare function gradient-name($base-gradient as element(),
$name as xs:string) as element()
declare function gradient-name($base-gradient as element(), $name as xs:string) as element()
Params
- base-gradient as element()
- name as xs:string
Returns
- element()
declare function this:gradient-name( $base-gradient as element(), $name as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-name($base-gradient, $name) else this:linear-name($base-gradient, $name) }
Function: gradient-reverse
declare function gradient-reverse($base-gradient as element()) as element()
declare function gradient-reverse($base-gradient as element()) as element()
Params
- base-gradient as element()
Returns
- element()
declare function this:gradient-reverse( $base-gradient as element() ) as element() { if (this:is-radial($base-gradient)) then this:radial-reverse($base-gradient) else this:linear-reverse($base-gradient) }
Function: gradient-reverse
declare function gradient-reverse($name as xs:string,
$base-gradient as element()) as element()
declare function gradient-reverse($name as xs:string, $base-gradient as element()) as element()
Params
- name as xs:string
- base-gradient as element()
Returns
- element()
declare %art:deprecated function this:gradient-reverse( $name as xs:string, $base-gradient as element() ) as element() { if (this:is-radial($base-gradient)) then this:radial-reverse($base-gradient)=>this:radial-name($name) else this:linear-reverse($base-gradient)=>this:linear-name($name) }
Function: gradient-random
declare function gradient-random($base-gradient as element()) as element()
declare function gradient-random($base-gradient as element()) as element()
Params
- base-gradient as element()
Returns
- element()
declare function this:gradient-random( $base-gradient as element() ) as element() { if (this:is-radial($base-gradient)) then this:radial-random($base-gradient) else this:linear-random($base-gradient) }
Function: gradient-random
declare function gradient-random($name as xs:string,
$base-gradient as element()) as element()
declare function gradient-random($name as xs:string, $base-gradient as element()) as element()
Params
- name as xs:string
- base-gradient as element()
Returns
- element()
declare %art:deprecated function this:gradient-random( $name as xs:string, $base-gradient as element() ) as element() { if (this:is-radial($base-gradient)) then this:radial-random($base-gradient)=>this:radial-name($name) else this:linear-random($base-gradient)=>this:linear-name($name) }
Function: gradient-sample
declare function gradient-sample($base-gradient as element(),
$num as xs:integer) as element()
declare function gradient-sample($base-gradient as element(), $num as xs:integer) as element()
Params
- base-gradient as element()
- num as xs:integer
Returns
- element()
declare function this:gradient-sample( $base-gradient as element(), $num as xs:integer ) as element() { if (this:is-radial($base-gradient)) then this:radial-sample($base-gradient, $num) else this:linear-sample($base-gradient, $num) }
Function: gradient-sample
declare function gradient-sample($name as xs:string,
$base-gradient as element(),
$num as xs:integer) as element()
declare function gradient-sample($name as xs:string, $base-gradient as element(), $num as xs:integer) as element()
Params
- name as xs:string
- base-gradient as element()
- num as xs:integer
Returns
- element()
declare %art:deprecated function this:gradient-sample( $name as xs:string, $base-gradient as element(), $num as xs:integer ) as element() { if (this:is-radial($base-gradient)) then this:radial-sample($base-gradient, $num)=>this:radial-name($name) else this:linear-sample($base-gradient, $num)=>this:linear-name($name) }
Function: gradient-fade
declare function gradient-fade($base-gradient as element(),
$start-opacity as xs:double,
$end-opacity as xs:double) as element()
declare function gradient-fade($base-gradient as element(), $start-opacity as xs:double, $end-opacity as xs:double) as element()
Params
- base-gradient as element()
- start-opacity as xs:double
- end-opacity as xs:double
Returns
- element()
declare function this:gradient-fade( $base-gradient as element(), $start-opacity as xs:double, $end-opacity as xs:double ) as element() { if (this:is-radial($base-gradient)) then this:radial-fade($base-gradient, $start-opacity, $end-opacity) else this:linear-fade($base-gradient, $start-opacity, $end-opacity) }
Function: gradient-fade
declare function gradient-fade($name as xs:string,
$base-gradient as element(),
$start-opacity as xs:double,
$end-opacity as xs:double) as element()
declare function gradient-fade($name as xs:string, $base-gradient as element(), $start-opacity as xs:double, $end-opacity as xs:double) as element()
Params
- name as xs:string
- base-gradient as element()
- start-opacity as xs:double
- end-opacity as xs:double
Returns
- element()
declare %art:deprecated function this:gradient-fade( $name as xs:string, $base-gradient as element(), $start-opacity as xs:double, $end-opacity as xs:double ) as element() { if (this:is-radial($base-gradient)) then this:radial-fade($base-gradient, $start-opacity, $end-opacity)=>this:radial-name($name) else this:linear-fade($base-gradient, $start-opacity, $end-opacity)=>this:linear-name($name) }
Function: gradient-transform
declare function gradient-transform($base-gradient as element(),
$transform as xs:string) as element()
declare function gradient-transform($base-gradient as element(), $transform as xs:string) as element()
Params
- base-gradient as element()
- transform as xs:string
Returns
- element()
declare function this:gradient-transform( $base-gradient as element(), $transform as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-transform($base-gradient, $transform) else this:linear-transform($base-gradient, $transform) }
Function: gradient-transform
declare function gradient-transform($name as xs:string,
$base-gradient as element(),
$transform as xs:string) as element()
declare function gradient-transform($name as xs:string, $base-gradient as element(), $transform as xs:string) as element()
Params
- name as xs:string
- base-gradient as element()
- transform as xs:string
Returns
- element()
declare %art:deprecated function this:gradient-transform( $name as xs:string, $base-gradient as element(), $transform as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-transform($base-gradient, $transform)=>this:radial-name($name) else this:linear-transform($base-gradient, $transform)=>this:linear-name($name) }
Function: gradient-units
declare function gradient-units($base-gradient as element(),
$objectBoundingBox as xs:boolean) as element()
declare function gradient-units($base-gradient as element(), $objectBoundingBox as xs:boolean) as element()
Params
- base-gradient as element()
- objectBoundingBox as xs:boolean
Returns
- element()
declare function this:gradient-units( $base-gradient as element(), $objectBoundingBox as xs:boolean ) as element() { if (this:is-radial($base-gradient)) then this:radial-units($base-gradient, $objectBoundingBox) else this:linear-units($base-gradient, $objectBoundingBox) }
Function: gradient-units
declare function gradient-units($name as xs:string,
$base-gradient as element(),
$objectBoundingBox as xs:boolean) as element()
declare function gradient-units($name as xs:string, $base-gradient as element(), $objectBoundingBox as xs:boolean) as element()
Params
- name as xs:string
- base-gradient as element()
- objectBoundingBox as xs:boolean
Returns
- element()
declare %art:deprecated function this:gradient-units( $name as xs:string, $base-gradient as element(), $objectBoundingBox as xs:boolean ) as element() { if (this:is-radial($base-gradient)) then this:radial-units($base-gradient, $objectBoundingBox)=>this:radial-name($name) else this:linear-units($base-gradient, $objectBoundingBox)=>this:linear-name($name) }
Function: gradient-spread
declare function gradient-spread($base-gradient as element(),
$method as xs:string) as element()
declare function gradient-spread($base-gradient as element(), $method as xs:string) as element()
Params
- base-gradient as element()
- method as xs:string
Returns
- element()
declare function this:gradient-spread( $base-gradient as element(), $method as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-spread($base-gradient, $method) else this:linear-spread($base-gradient, $method) }
Function: gradient-spread
declare function gradient-spread($name as xs:string,
$base-gradient as element(),
$method as xs:string) as element()
declare function gradient-spread($name as xs:string, $base-gradient as element(), $method as xs:string) as element()
Params
- name as xs:string
- base-gradient as element()
- method as xs:string
Returns
- element()
declare %art:deprecated function this:gradient-spread( $name as xs:string, $base-gradient as element(), $method as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-spread($base-gradient, $method)=>this:radial-name($name) else this:linear-spread($base-gradient, $method)=>this:linear-name($name) }
Function: gradient-layout
declare function gradient-layout($base-gradient as element(),
$layout as xs:integer*) as element()
declare function gradient-layout($base-gradient as element(), $layout as xs:integer*) as element()
Params
- base-gradient as element()
- layout as xs:integer*
Returns
- element()
declare function this:gradient-layout( $base-gradient as element(), $layout as xs:integer* ) as element() { if (this:is-radial($base-gradient)) then this:radial-layout($base-gradient, $layout) else this:linear-layout($base-gradient, $layout) }
Function: gradient-layout
declare function gradient-layout($name as xs:string,
$base-gradient as element(),
$layout as xs:integer*) as element()
declare function gradient-layout($name as xs:string, $base-gradient as element(), $layout as xs:integer*) as element()
Params
- name as xs:string
- base-gradient as element()
- layout as xs:integer*
Returns
- element()
declare %art:deprecated function this:gradient-layout( $name as xs:string, $base-gradient as element(), $layout as xs:integer* ) as element() { if (this:is-radial($base-gradient)) then this:radial-layout($base-gradient, $layout)=>this:radial-name($name) else this:linear-layout($base-gradient, $layout)=>this:linear-name($name) }
Function: gradient-interleave
declare function gradient-interleave($base-gradient as element(),
$interleave as xs:string) as element()
declare function gradient-interleave($base-gradient as element(), $interleave as xs:string) as element()
Params
- base-gradient as element()
- interleave as xs:string
Returns
- element()
declare function this:gradient-interleave( $base-gradient as element(), $interleave as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-interleave($base-gradient, $interleave) else this:linear-interleave($base-gradient, $interleave) }
Function: gradient-interleave
declare function gradient-interleave($name as xs:string,
$base-gradient as element(),
$interleave as xs:string) as element()
declare function gradient-interleave($name as xs:string, $base-gradient as element(), $interleave as xs:string) as element()
Params
- name as xs:string
- base-gradient as element()
- interleave as xs:string
Returns
- element()
declare %art:deprecated function this:gradient-interleave( $name as xs:string, $base-gradient as element(), $interleave as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-interleave($base-gradient, $interleave)=>this:radial-name($name) else this:linear-interleave($base-gradient, $interleave)=>this:linear-name($name) }
Function: gradient-opacity
declare function gradient-opacity($base-gradient as element(),
$opacity as function(xs:integer) as xs:double) as element()
declare function gradient-opacity($base-gradient as element(), $opacity as function(xs:integer) as xs:double) as element()
Params
- base-gradient as element()
- opacity as function(xs:integer)asxs:double
Returns
- element()
declare function this:gradient-opacity( $base-gradient as element(), $opacity as function(xs:integer) as xs:double ) as element() { if (this:is-radial($base-gradient)) then this:radial-opacity($base-gradient, $opacity) else this:linear-opacity($base-gradient, $opacity) }
Function: gradient-opacity
declare function gradient-opacity($name as xs:string,
$base-gradient as element(),
$opacity as function(xs:integer) as xs:double) as element()
declare function gradient-opacity($name as xs:string, $base-gradient as element(), $opacity as function(xs:integer) as xs:double) as element()
Params
- name as xs:string
- base-gradient as element()
- opacity as function(xs:integer)asxs:double
Returns
- element()
declare %art:deprecated function this:gradient-opacity( $name as xs:string, $base-gradient as element(), $opacity as function(xs:integer) as xs:double ) as element() { if (this:is-radial($base-gradient)) then this:radial-opacity($base-gradient, $opacity)=>this:radial-name($name) else this:linear-opacity($base-gradient, $opacity)=>this:linear-name($name) }
Function: gradient-colour
declare function gradient-colour($base-gradient as element(),
$colour as function(xs:integer) as xs:string) as element()
declare function gradient-colour($base-gradient as element(), $colour as function(xs:integer) as xs:string) as element()
Params
- base-gradient as element()
- colour as function(xs:integer)asxs:string
Returns
- element()
declare function this:gradient-colour( $base-gradient as element(), $colour as function(xs:integer) as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-colour($base-gradient, $colour) else this:linear-colour($base-gradient, $colour) }
Function: gradient-colour
declare function gradient-colour($name as xs:string,
$base-gradient as element(),
$colour as function(xs:integer) as xs:string) as element()
declare function gradient-colour($name as xs:string, $base-gradient as element(), $colour as function(xs:integer) as xs:string) as element()
Params
- name as xs:string
- base-gradient as element()
- colour as function(xs:integer)asxs:string
Returns
- element()
declare %art:deprecated function this:gradient-colour( $name as xs:string, $base-gradient as element(), $colour as function(xs:integer) as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-colour($base-gradient, $colour)=>this:radial-name($name) else this:linear-colour($base-gradient, $colour)=>this:linear-name($name) }
Function: gradient-ref
declare function gradient-ref($name as xs:string,
$base-gradient-name as xs:string) as element()
declare function gradient-ref($name as xs:string, $base-gradient-name as xs:string) as element()
Params
- name as xs:string
- base-gradient-name as xs:string
Returns
- element()
declare function this:gradient-ref( $name as xs:string, $base-gradient-name as xs:string ) as element() { let $def := this:gradient($base-gradient-name) return ( if (exists($def) and this:is-radial($def)) then this:radial-ref($name, $base-gradient-name) else this:linear-ref($name, $base-gradient-name) ) }
Function: linear-name
declare function linear-name($base-gradient as element(svg:linearGradient),
$name as xs:string) as element(svg:linearGradient)
declare function linear-name($base-gradient as element(svg:linearGradient), $name as xs:string) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- name as xs:string
Returns
- element(svg:linearGradient)
declare function this:linear-name( $base-gradient as element(svg:linearGradient), $name as xs:string ) as element(svg:linearGradient) { <svg:linearGradient id="{this:safe($name)}">{ $base-gradient/(@* except @id), $base-gradient/* }</svg:linearGradient> }
Function: linear-reverse
declare function linear-reverse($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
declare function linear-reverse($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:linearGradient)
declare function this:linear-reverse( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$base-gradient/@x2}" x2="{$base-gradient/@x1}" y1="{$base-gradient/@y2}" y2="{$base-gradient/@y1}">{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }
Function: linear-flip
declare function linear-flip($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
declare function linear-flip($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:linearGradient)
declare function this:linear-flip( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$base-gradient/@y1}" x2="{$base-gradient/@y2}" y1="{$base-gradient/@x1}" y2="{$base-gradient/@x2}" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }
Function: linear-invert
declare function linear-invert($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
declare function linear-invert($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:linearGradient)
declare function this:linear-invert( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$base-gradient/@y1}" x2="{$base-gradient/@y2}" y1="{$base-gradient/@x2}" y2="{$base-gradient/@x1}" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }
Function: linear-full
declare function linear-full($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
declare function linear-full($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:linearGradient)
declare function this:linear-full( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient y1="{$base-gradient/@x1}" y2="{$base-gradient/@x2}" >{ $base-gradient/(@* except (@y1|@y2)), $base-gradient/* }</svg:linearGradient> }
Function: linear-anti
declare function linear-anti($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
declare function linear-anti($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:linearGradient)
declare function this:linear-anti( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$base-gradient/@x2}" x2="{$base-gradient/@x1}" y1="{$base-gradient/@x2}" y2="{$base-gradient/@x1}" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }
Function: linear-slant
declare function linear-slant($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
declare function linear-slant($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:linearGradient)
declare function this:linear-slant( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="0%" x2="50%" y1="0%" y2="90%" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }
Function: linear-adjust
declare function linear-adjust($base-gradient as element(svg:linearGradient),
$xys as xs:integer*) as element(svg:linearGradient)
declare function linear-adjust($base-gradient as element(svg:linearGradient), $xys as xs:integer*) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- xys as xs:integer*
Returns
- element(svg:linearGradient)
declare function this:linear-adjust( $base-gradient as element(svg:linearGradient), $xys as xs:integer* (: x1, y1, x2, y2 :) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$xys[1]}%" x2="{$xys[3]}%" y1="{$xys[2]}%" y2="{$xys[4]}%" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }
Function: linear-random
declare function linear-random($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
declare function linear-random($base-gradient as element(svg:linearGradient)) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:linearGradient)
declare function this:linear-random( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { let $stops := rand:shuffle($base-gradient/svg:stop) let $percentages := util:linspace(count($stops), 0, 100)!util:decimal(., 2) return <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $stops return ( <svg:stop offset="{$percentages[$i]}%">{ $stop/(@* except @offset) }</svg:stop> ) }</svg:linearGradient> }
Function: linear-interleave
declare function linear-interleave($base-gradient as element(svg:linearGradient),
$interleave as xs:string) as element(svg:linearGradient)
declare function linear-interleave($base-gradient as element(svg:linearGradient), $interleave as xs:string) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- interleave as xs:string
Returns
- element(svg:linearGradient)
declare function this:linear-interleave( $base-gradient as element(svg:linearGradient), $interleave as xs:string ) as element(svg:linearGradient) { <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop return ( if ($i mod 2 = 0) then $stop else ( <svg:stop stop-color="{$interleave}">{ $stop/(@* except @stop-color) }</svg:stop> ) ) }</svg:linearGradient> }
Function: linear-opacity
declare function linear-opacity($base-gradient as element(svg:linearGradient),
$opacity as function(xs:integer) as xs:double) as element(svg:linearGradient)
declare function linear-opacity($base-gradient as element(svg:linearGradient), $opacity as function(xs:integer) as xs:double) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- opacity as function(xs:integer)asxs:double
Returns
- element(svg:linearGradient)
declare function this:linear-opacity( $base-gradient as element(svg:linearGradient), $opacity as function(xs:integer) as xs:double ) as element(svg:linearGradient) { <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $stop-opacity := util:decimal(util:clamp($opacity($i),0,1),3) return ( <svg:stop stop-opacity="{$stop-opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:linearGradient> }
Function: linear-colour
declare function linear-colour($base-gradient as element(svg:linearGradient),
$colour as function(xs:integer) as xs:string) as element(svg:linearGradient)
declare function linear-colour($base-gradient as element(svg:linearGradient), $colour as function(xs:integer) as xs:string) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- colour as function(xs:integer)asxs:string
Returns
- element(svg:linearGradient)
declare function this:linear-colour( $base-gradient as element(svg:linearGradient), $colour as function(xs:integer) as xs:string ) as element(svg:linearGradient) { <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $stop-colour := $colour($i) return ( <svg:stop stop-color="{$stop-colour}">{ $stop/(@* except @stop-color) }</svg:stop> ) }</svg:linearGradient> }
Function: linear-sample
declare function linear-sample($base-gradient as element(svg:linearGradient),
$num as xs:integer) as element(svg:linearGradient)
declare function linear-sample($base-gradient as element(svg:linearGradient), $num as xs:integer) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- num as xs:integer
Returns
- element(svg:linearGradient)
declare function this:linear-sample( $base-gradient as element(svg:linearGradient), $num as xs:integer ) as element(svg:linearGradient) { if ($num <= 1) then ( <svg:linearGradient>{ $base-gradient/@*, $base-gradient/* }</svg:linearGradient> ) else ( let $stops := $base-gradient/svg:stop let $percentages := util:linspace($num, 0, 100)!util:decimal(., 2) let $sep := count($stops) div $num return if (count($stops) <= $num) then ( <svg:linearGradient>{ $base-gradient/@*, $base-gradient/* }</svg:linearGradient> ) else ( <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $i in 1 to $num let $stop := $stops[1 + ceiling($sep*($i - 1))] return ( <svg:stop offset="{$percentages[$i]}%">{ $stop/(@* except @offset) }</svg:stop> ) }</svg:linearGradient> ) ) }
Function: linear-fade
declare function linear-fade($base-gradient as element(svg:linearGradient),
$start-opacity as xs:double,
$end-opacity as xs:double) as element(svg:linearGradient)
declare function linear-fade($base-gradient as element(svg:linearGradient), $start-opacity as xs:double, $end-opacity as xs:double) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- start-opacity as xs:double
- end-opacity as xs:double
Returns
- element(svg:linearGradient)
declare function this:linear-fade( $base-gradient as element(svg:linearGradient), $start-opacity as xs:double, $end-opacity as xs:double ) as element(svg:linearGradient) { let $n := count($base-gradient/svg:stop) let $delta := ($end-opacity - $start-opacity) div ($n - 1) return <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) return ( <svg:stop stop-opacity="{$opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:linearGradient> }
Function: linear-symmetric-fade
declare function linear-symmetric-fade($base-gradient as element(svg:linearGradient),
$start-opacity as xs:double,
$peak-opacity as xs:double,
$end-opacity as xs:double) as element(svg:linearGradient)
declare function linear-symmetric-fade($base-gradient as element(svg:linearGradient), $start-opacity as xs:double, $peak-opacity as xs:double, $end-opacity as xs:double) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- start-opacity as xs:double
- peak-opacity as xs:double
- end-opacity as xs:double
Returns
- element(svg:linearGradient)
declare function this:linear-symmetric-fade( $base-gradient as element(svg:linearGradient), $start-opacity as xs:double, $peak-opacity as xs:double, $end-opacity as xs:double ) as element(svg:linearGradient) { let $n := count($base-gradient/svg:stop) let $half := $n idiv 2 let $delta1 := ($peak-opacity - $start-opacity) div ($n idiv 2) let $delta2 := ($end-opacity - $peak-opacity) div (($n + 1) idiv 2) return <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $opacity := if ($i <= $half) then util:decimal($start-opacity + $delta1*($i - 1), 2) else util:decimal($peak-opacity + $delta2*($i - $half - 1), 2) return ( <svg:stop stop-opacity="{$opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:linearGradient> }
Function: linear-transform
declare function linear-transform($base-gradient as element(svg:linearGradient),
$transform as xs:string) as element(svg:linearGradient)
declare function linear-transform($base-gradient as element(svg:linearGradient), $transform as xs:string) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- transform as xs:string
Returns
- element(svg:linearGradient)
declare function this:linear-transform( $base-gradient as element(svg:linearGradient), $transform as xs:string ) as element(svg:linearGradient) { <svg:linearGradient gradientTransform="{$transform}">{ $base-gradient/(@* except @gradientTransform), $base-gradient/* }</svg:linearGradient> }
Function: linear-units
declare function linear-units($base-gradient as element(svg:linearGradient),
$objectBoundingBox as xs:boolean) as element(svg:linearGradient)
declare function linear-units($base-gradient as element(svg:linearGradient), $objectBoundingBox as xs:boolean) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- objectBoundingBox as xs:boolean
Returns
- element(svg:linearGradient)
declare function this:linear-units( $base-gradient as element(svg:linearGradient), $objectBoundingBox as xs:boolean ) as element(svg:linearGradient) { let $val := if ($objectBoundingBox) then "objectBoundingBox" else "userSpaceOnUse" return <svg:linearGradient gradientUnits="{$val}">{ $base-gradient/(@* except @gradientUnits), $base-gradient/* }</svg:linearGradient> }
Function: linear-spread
declare function linear-spread($base-gradient as element(svg:linearGradient),
$method as xs:string) as element(svg:linearGradient)
declare function linear-spread($base-gradient as element(svg:linearGradient), $method as xs:string) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- method as xs:string
Returns
- element(svg:linearGradient)
declare function this:linear-spread( $base-gradient as element(svg:linearGradient), $method as xs:string ) as element(svg:linearGradient) { <svg:linearGradient spreadMethod="{$method}">{ $base-gradient/(@* except @spreadMethod), $base-gradient/* }</svg:linearGradient> }
Function: linear-layout
declare function linear-layout($base-gradient as element(svg:linearGradient),
$layout as xs:integer*) as element(svg:linearGradient)
declare function linear-layout($base-gradient as element(svg:linearGradient), $layout as xs:integer*) as element(svg:linearGradient)
Params
- base-gradient as element(svg:linearGradient)
- layout as xs:integer*
Returns
- element(svg:linearGradient)
declare function this:linear-layout( $base-gradient as element(svg:linearGradient), $layout as xs:integer* ) as element(svg:linearGradient) { let $x1 := ($layout[1],0)[1] let $x2 := ($layout[2],100)[1] let $y1 := ($layout[3],0)[1] let $y2 := ($layout[4],0)[1] return <svg:linearGradient x1="{$x1}%" x2="{$x2}%" y1="{$y1}%" y2="{$y2}%">{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }
Function: linear-ref
declare function linear-ref($name as xs:string,
$base-gradient-name as xs:string) as element(svg:linearGradient)
declare function linear-ref($name as xs:string, $base-gradient-name as xs:string) as element(svg:linearGradient)
Params
- name as xs:string
- base-gradient-name as xs:string
Returns
- element(svg:linearGradient)
declare function this:linear-ref( $name as xs:string, $base-gradient-name as xs:string ) as element(svg:linearGradient) { <svg:linearGradient id="{this:safe($name)}" xlink:href="#{$base-gradient-name}"/> }
Function: radial-name
declare function radial-name($base-gradient as element(svg:radialGradient),
$name as xs:string) as element(svg:radialGradient)
declare function radial-name($base-gradient as element(svg:radialGradient), $name as xs:string) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- name as xs:string
Returns
- element(svg:radialGradient)
declare function this:radial-name( $base-gradient as element(svg:radialGradient), $name as xs:string ) as element(svg:radialGradient) { <svg:radialGradient id="{this:safe($name)}">{ $base-gradient/(@* except @id), $base-gradient/* }</svg:radialGradient> }
Function: radial-outflow
declare function radial-outflow($base-gradient as element(svg:linearGradient)) as element(svg:radialGradient)
declare function radial-outflow($base-gradient as element(svg:linearGradient)) as element(svg:radialGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:radialGradient)
declare function this:radial-outflow( $base-gradient as element(svg:linearGradient) ) as element(svg:radialGradient) { <svg:radialGradient cx="50%" cy="50%" r="50%">{ $base-gradient/(@* except (@r|@cx|@cy)), $base-gradient/* }</svg:radialGradient> }
Function: radial-inflow
declare function radial-inflow($base-gradient as element(svg:linearGradient)) as element(svg:radialGradient)
declare function radial-inflow($base-gradient as element(svg:linearGradient)) as element(svg:radialGradient)
Params
- base-gradient as element(svg:linearGradient)
Returns
- element(svg:radialGradient)
declare function this:radial-inflow( $base-gradient as element(svg:linearGradient) ) as element(svg:radialGradient) { let $offsets := reverse( for $offset in $base-gradient/svg:stop/@offset return if (contains($offset,"%")) then util:decimal(100.0 - number(substring-before($offset,"%")), 2) else util:decimal(100.0 - number($offset), 2) ) let $colours := reverse($base-gradient/svg:stop/@stop-color) let $opacities := reverse($base-gradient/svg:stop/@stop-opacity) return ( <svg:radialGradient cx="50%" cy="50%" r="50%">{ $base-gradient/(@* except (@r|@cx|@cy)), $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop return ( <svg:stop offset="{$offsets[$i]}%" stop-color="{$colours[$i]}" stop-opacity="{$opacities[$i]}"/> ) }</svg:radialGradient> ) }
Function: radial-reverse
declare function radial-reverse($base-gradient as element(svg:radialGradient)) as element(svg:radialGradient)
declare function radial-reverse($base-gradient as element(svg:radialGradient)) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
Returns
- element(svg:radialGradient)
declare function this:radial-reverse( $base-gradient as element(svg:radialGradient) ) as element(svg:radialGradient) { let $offsets := reverse( for $offset in $base-gradient/svg:stop/@offset return if (contains($offset,"%")) then util:decimal(100.0 - number(substring-before($offset,"%")), 2) else util:decimal(100.0 - number($offset), 2) ) let $colours := reverse($base-gradient/svg:stop/@stop-color) let $opacities := reverse($base-gradient/svg:stop/@stop-opacity) return ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop return ( <svg:stop offset="{$offsets[$i]}%" stop-color="{$colours[$i]}" stop-opacity="{$opacities[$i]}"/> ) }</svg:radialGradient> ) }
Function: radial-random
declare function radial-random($base-gradient as element(svg:radialGradient)) as element(svg:radialGradient)
declare function radial-random($base-gradient as element(svg:radialGradient)) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
Returns
- element(svg:radialGradient)
declare function this:radial-random( $base-gradient as element(svg:radialGradient) ) as element(svg:radialGradient) { let $stops := rand:shuffle($base-gradient/svg:stop) let $percentages := util:linspace(count($stops), 0, 100)!util:decimal(., 2) return ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $stops return ( <svg:stop offset="{$percentages[$i]}%">{ $stop/(@* except @offset) }</svg:stop> ) }</svg:radialGradient> ) }
Function: radial-interleave
declare function radial-interleave($base-gradient as element(svg:radialGradient),
$interleave as xs:string) as element(svg:radialGradient)
declare function radial-interleave($base-gradient as element(svg:radialGradient), $interleave as xs:string) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- interleave as xs:string
Returns
- element(svg:radialGradient)
declare function this:radial-interleave( $base-gradient as element(svg:radialGradient), $interleave as xs:string ) as element(svg:radialGradient) { <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop return ( if ($i mod 2 = 0) then $stop else ( <svg:stop stop-color="{$interleave}">{ $stop/(@* except @stop-color) }</svg:stop> ) ) }</svg:radialGradient> }
Function: radial-opacity
declare function radial-opacity($base-gradient as element(svg:radialGradient),
$opacity as function(xs:integer) as xs:double) as element(svg:radialGradient)
declare function radial-opacity($base-gradient as element(svg:radialGradient), $opacity as function(xs:integer) as xs:double) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- opacity as function(xs:integer)asxs:double
Returns
- element(svg:radialGradient)
declare function this:radial-opacity( $base-gradient as element(svg:radialGradient), $opacity as function(xs:integer) as xs:double ) as element(svg:radialGradient) { <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $stop-opacity := util:decimal(util:clamp($opacity($i),0,1),3) return ( <svg:stop stop-opacity="{$stop-opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:radialGradient> }
Function: radial-colour
declare function radial-colour($base-gradient as element(svg:radialGradient),
$colour as function(xs:integer) as xs:string) as element(svg:radialGradient)
declare function radial-colour($base-gradient as element(svg:radialGradient), $colour as function(xs:integer) as xs:string) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- colour as function(xs:integer)asxs:string
Returns
- element(svg:radialGradient)
declare function this:radial-colour( $base-gradient as element(svg:radialGradient), $colour as function(xs:integer) as xs:string ) as element(svg:radialGradient) { <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $stop-colour := $colour($i) return ( <svg:stop stop-color="{$stop-colour}">{ $stop/(@* except @stop-color) }</svg:stop> ) }</svg:radialGradient> }
Function: radial-sample
declare function radial-sample($base-gradient as element(svg:radialGradient),
$num as xs:integer) as element(svg:radialGradient)
declare function radial-sample($base-gradient as element(svg:radialGradient), $num as xs:integer) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- num as xs:integer
Returns
- element(svg:radialGradient)
declare function this:radial-sample( $base-gradient as element(svg:radialGradient), $num as xs:integer ) as element(svg:radialGradient) { if ($num <= 1) then ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/* }</svg:radialGradient> ) else ( let $stops := $base-gradient/svg:stop let $percentages := util:linspace($num, 0, 100)!util:decimal(., 2) let $sep := count($stops) div $num return if (count($stops) <= $num) then ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/* }</svg:radialGradient> ) else ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $i in 1 to $num let $stop := $stops[1 + ceiling($sep*($i - 1))] return ( <svg:stop offset="{$percentages[$i]}%">{$stop/(@* except @offset)}</svg:stop> ) }</svg:radialGradient> ) ) }
Function: radial-fade
declare function radial-fade($base-gradient as element(svg:radialGradient),
$start-opacity as xs:double,
$end-opacity as xs:double) as element(svg:radialGradient)
declare function radial-fade($base-gradient as element(svg:radialGradient), $start-opacity as xs:double, $end-opacity as xs:double) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- start-opacity as xs:double
- end-opacity as xs:double
Returns
- element(svg:radialGradient)
declare function this:radial-fade( $base-gradient as element(svg:radialGradient), $start-opacity as xs:double, $end-opacity as xs:double ) as element(svg:radialGradient) { let $n := count($base-gradient/svg:stop) let $delta := ($end-opacity - $start-opacity) div $n return <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) return ( <svg:stop stop-opacity="{$opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:radialGradient> }
Function: radial-units
declare function radial-units($base-gradient as element(svg:radialGradient),
$objectBoundingBox as xs:boolean) as element(svg:radialGradient)
declare function radial-units($base-gradient as element(svg:radialGradient), $objectBoundingBox as xs:boolean) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- objectBoundingBox as xs:boolean
Returns
- element(svg:radialGradient)
declare function this:radial-units( $base-gradient as element(svg:radialGradient), $objectBoundingBox as xs:boolean ) as element(svg:radialGradient) { let $val := if ($objectBoundingBox) then "objectBoundingBox" else "userSpaceOnUse" return <svg:radialGradient gradientUnits="{$val}">{ $base-gradient/(@* except @gradientUnits), $base-gradient/* }</svg:radialGradient> }
Function: radial-center
declare function radial-center($base-gradient as element(svg:radialGradient),
$cx-percent as xs:integer,
$cy-percent as xs:integer) as element(svg:radialGradient)
declare function radial-center($base-gradient as element(svg:radialGradient), $cx-percent as xs:integer, $cy-percent as xs:integer) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- cx-percent as xs:integer
- cy-percent as xs:integer
Returns
- element(svg:radialGradient)
declare function this:radial-center( $base-gradient as element(svg:radialGradient), $cx-percent as xs:integer, $cy-percent as xs:integer ) as element(svg:radialGradient) { <svg:radialGradient cx="{$cx-percent}%" cy="{$cy-percent}%">{ $base-gradient/(@* except (@cx|@cy)), $base-gradient/* }</svg:radialGradient> }
Function: radial-focus
declare function radial-focus($base-gradient as element(svg:radialGradient),
$fx-percent as xs:integer,
$fy-percent as xs:integer) as element(svg:radialGradient)
declare function radial-focus($base-gradient as element(svg:radialGradient), $fx-percent as xs:integer, $fy-percent as xs:integer) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- fx-percent as xs:integer
- fy-percent as xs:integer
Returns
- element(svg:radialGradient)
declare function this:radial-focus( $base-gradient as element(svg:radialGradient), $fx-percent as xs:integer, $fy-percent as xs:integer ) as element(svg:radialGradient) { <svg:radialGradient fx="{$fx-percent}%" fy="{$fy-percent}%">{ $base-gradient/(@* except (@fx|@fy)), $base-gradient/* }</svg:radialGradient> }
Function: radial-radius
declare function radial-radius($base-gradient as element(svg:radialGradient),
$r-percent as xs:integer) as element(svg:radialGradient)
declare function radial-radius($base-gradient as element(svg:radialGradient), $r-percent as xs:integer) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- r-percent as xs:integer
Returns
- element(svg:radialGradient)
declare function this:radial-radius( $base-gradient as element(svg:radialGradient), $r-percent as xs:integer ) as element(svg:radialGradient) { <svg:radialGradient r="{$r-percent}%">{ $base-gradient/(@* except (@r)), $base-gradient/* }</svg:radialGradient> }
Function: radial-circle
declare function radial-circle($base-gradient as element(svg:radialGradient),
$radial-circle as map(xs:string,item()*)) as element(svg:radialGradient)
declare function radial-circle($base-gradient as element(svg:radialGradient), $radial-circle as map(xs:string,item()*)) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- radial-circle as map(xs:string,item()*)
Returns
- element(svg:radialGradient)
declare function this:radial-circle( $base-gradient as element(svg:radialGradient), $radial-circle as map(xs:string,item()*) (: all as percents :) ) as element(svg:radialGradient) { let $r := ellipse:radius($radial-circle) let $center := ellipse:center($radial-circle) let $cx := point:x($center) let $cy := point:y($center) return <svg:radialGradient cx="{$cx}%" cy="{$cy}%" r="{$r}%">{ $base-gradient/(@* except (@r|@cx|@cy)), $base-gradient/* }</svg:radialGradient> }
Function: radial-transform
declare function radial-transform($base-gradient as element(svg:radialGradient),
$transform as xs:string) as element(svg:radialGradient)
declare function radial-transform($base-gradient as element(svg:radialGradient), $transform as xs:string) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- transform as xs:string
Returns
- element(svg:radialGradient)
declare function this:radial-transform( $base-gradient as element(svg:radialGradient), $transform as xs:string ) as element(svg:radialGradient) { <svg:radialGradient gradientTransform="{$transform}">{ $base-gradient/(@* except @gradientTransform), $base-gradient/* }</svg:radialGradient> }
Function: radial-spread
declare function radial-spread($base-gradient as element(svg:radialGradient),
$method as xs:string) as element(svg:radialGradient)
declare function radial-spread($base-gradient as element(svg:radialGradient), $method as xs:string) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- method as xs:string
Returns
- element(svg:radialGradient)
declare function this:radial-spread( $base-gradient as element(svg:radialGradient), $method as xs:string ) as element(svg:radialGradient) { <svg:radialGradient spreadMethod="{$method}">{ $base-gradient/(@* except @spreadMethod), $base-gradient/* }</svg:radialGradient> }
Function: radial-layout
declare function radial-layout($base-gradient as element(svg:radialGradient),
$layout as xs:integer*) as element(svg:radialGradient)
declare function radial-layout($base-gradient as element(svg:radialGradient), $layout as xs:integer*) as element(svg:radialGradient)
Params
- base-gradient as element(svg:radialGradient)
- layout as xs:integer*
Returns
- element(svg:radialGradient)
declare function this:radial-layout( $base-gradient as element(svg:radialGradient), $layout as xs:integer* (: r, cx, cy :) ) as element(svg:radialGradient) { let $r := ($layout[1],50)[1] let $cx := ($layout[2],50)[1] let $cy := ($layout[3],$cx)[1] return <svg:radialGradient cx="{$cx}%" cy="{$cy}%" r="{$r}%">{ $base-gradient/(@* except (@r|@cx|@cy)), $base-gradient/* }</svg:radialGradient> }
Function: radial-ref
declare function radial-ref($name as xs:string,
$base-gradient-name as xs:string) as element(svg:radialGradient)
declare function radial-ref($name as xs:string, $base-gradient-name as xs:string) as element(svg:radialGradient)
Params
- name as xs:string
- base-gradient-name as xs:string
Returns
- element(svg:radialGradient)
declare function this:radial-ref( $name as xs:string, $base-gradient-name as xs:string ) as element(svg:radialGradient) { <svg:radialGradient id="{this:safe($name)}" xlink:href="#{$base-gradient-name}"/> }
Function: expand-evenly
declare function expand-evenly($num-stops as xs:integer,
$base-rgb-str as xs:string*,
$do-spline as xs:boolean) as xs:string*
declare function expand-evenly($num-stops as xs:integer, $base-rgb-str as xs:string*, $do-spline as xs:boolean) as xs:string*
Params
- num-stops as xs:integer
- base-rgb-str as xs:string*
- do-spline as xs:boolean
Returns
- xs:string*
declare function this:expand-evenly( $num-stops as xs:integer, $base-rgb-str as xs:string*, $do-spline as xs:boolean ) as xs:string* { (: To end up w/ 512 stops, we need to add n interpolations to each edge : we have ns stops already, so: : (ns - 1)*n + ns = 512 : n = (512 - ns)/(ns - 1) : We can only interpolate an integer number of points so that : leaves us a residue which we use as a probability of adding another point :) let $ns := count($base-rgb-str) let $n := ($num-stops - $ns) idiv ($ns - 1) let $residue := 100 * ($ns - 1) * ((($num-stops - $ns) div ($ns - 1)) - $n) let $base-rgb := $base-rgb-str!rgb:rgb(.) let $base-hsl := $base-rgb!cs:rgb-to-hsluv(.) let $expanded-hsl := ( $base-hsl[1], let $edges := if ($do-spline) then ( spline:spline($base-hsl)=>path:edges() ) else ( edge:to-edges($base-hsl) ) for $edge in $edges let $n-interp := if (rand:flip($residue)) then $n + 1 else $n let $interp := tail(edge:interpolate($n-interp, $edge)) return $interp ) let $expanded-rgb := $expanded-hsl!cs:hsluv-to-rgb(.) let $expanded-rgb-str := $expanded-rgb!rgb:to-string(.) return $expanded-rgb-str }
Function: expand-scaled
declare function expand-scaled($num-stops as xs:integer,
$base-rgb-str as xs:string*,
$base-percents as xs:double*,
$do-spline as xs:boolean) as xs:string*
declare function expand-scaled($num-stops as xs:integer, $base-rgb-str as xs:string*, $base-percents as xs:double*, $do-spline as xs:boolean) as xs:string*
Params
- num-stops as xs:integer
- base-rgb-str as xs:string*
- base-percents as xs:double*
- do-spline as xs:boolean
Returns
- xs:string*
declare function this:expand-scaled( $num-stops as xs:integer, $base-rgb-str as xs:string*, $base-percents as xs:double*, $do-spline as xs:boolean ) as xs:string* { let $ns := count($base-rgb-str) let $edge-fractions := for $percent at $i in tail($base-percents) return ($percent - $base-percents[$i]) div 100.0 (: To end up w/ 512 stops, we need to add 512 - ns total : allocated in accordance with the percentage differences for each : edge : Use $num-stops - 1 because the percentages end up including the end : points so that works better : We can only interpolate an integer number of points so that : leaves us a residue which we use as a probability of adding another point :) let $edge-ns := let $total := $num-stops - 1 (: $ns :) for $fraction in $edge-fractions return round($total * $fraction) cast as xs:integer let $residue := 100 * (($num-stops - (: $ns :) 1) - sum($edge-ns)) div ($ns - 1) let $base-rgb := $base-rgb-str!rgb:rgb(.) let $base-hsl := $base-rgb!cs:rgb-to-hsluv(.) let $expanded-hsl := ( $base-hsl[1], let $edges := if ($do-spline) then ( spline:spline($base-hsl)=>path:edges() ) else ( edge:to-edges($base-hsl) ) for $edge at $i in $edges let $n-interp := if (rand:flip($residue)) then $edge-ns[$i] + 1 else $edge-ns[$i] let $interp := tail(edge:interpolate($n-interp, $edge)) return $interp ) let $expanded-rgb := $expanded-hsl!cs:hsluv-to-rgb(.) let $expanded-rgb-str := $expanded-rgb!rgb:to-string(.) return $expanded-rgb-str }
Function: expand-gradient
declare function expand-gradient($num-stops as xs:integer,
$base-gradient as xs:string,
$do-even as xs:boolean,
$do-spline as xs:boolean) as xs:string*
declare function expand-gradient($num-stops as xs:integer, $base-gradient as xs:string, $do-even as xs:boolean, $do-spline as xs:boolean) as xs:string*
Params
- num-stops as xs:integer
- base-gradient as xs:string
- do-even as xs:boolean
- do-spline as xs:boolean
Returns
- xs:string*
declare function this:expand-gradient( $num-stops as xs:integer, $base-gradient as xs:string, $do-even as xs:boolean, $do-spline as xs:boolean ) as xs:string* { let $base-rgb-str := this:gradient-colours($base-gradient) let $base-percents := for $offset in this:gradient-stops($base-gradient)/@offset return xs:double(substring-before(string($offset),"%")) return if ($do-even) then this:expand-evenly($num-stops, $base-rgb-str, $do-spline) else this:expand-scaled($num-stops, $base-rgb-str, $base-percents, $do-spline) }
Original Source Code
xquery version "3.1"; (:~ : Module with functions providing some colouring support at the : algorithm level. : : Copyright© Mary Holstege 2020-2023 : CC-BY (https://creativecommons.org/licenses/by/4.0/) : @since October 2021 : @custom:Status Active :) module namespace this="http://mathling.com/svg/gradients"; declare namespace art="http://mathling.com/art"; declare namespace svg="http://www.w3.org/2000/svg"; declare namespace xsl="http://www.w3.org/1999/XSL/Transform"; declare namespace xhtml="http://www.w3.org/1999/xhtml"; declare namespace xlink="http://www.w3.org/1999/xlink"; declare namespace map="http://www.w3.org/2005/xpath-functions/map"; declare namespace math="http://www.w3.org/2005/xpath-functions/math"; import module namespace util="http://mathling.com/core/utilities" at "../core/utilities.xqy"; import module namespace rand="http://mathling.com/core/random" at "../core/random.xqy"; import module namespace point="http://mathling.com/geometric/point" at "../geo/point.xqy"; import module namespace edge="http://mathling.com/geometric/edge" at "../geo/edge.xqy"; import module namespace path="http://mathling.com/geometric/path" at "../geo/path.xqy"; import module namespace ellipse="http://mathling.com/geometric/ellipse" at "../geo/ellipse.xqy"; import module namespace spline="http://mathling.com/geometric/spline" at "../geo/spline.xqy"; import module namespace cs="http://mathling.com/colour/space" at "../colourspace/colour-space.xqy"; import module namespace rgb="http://mathling.com/colour/rgb" at "../colourspace/rgb.xqy"; import module namespace xyz="http://mathling.com/colour/xyz" at "../colourspace/xyz.xqy"; import module namespace tone="http://mathling.com/colour/tonemap" at "../colourspace/tonemap.xqy"; import module namespace stop="http://mathling.com/colour/stop" at "../colourspace/stop.xqy"; declare variable $this:NAMED-GRADIENTS as xs:string* := ( "acton", "bam", "bamako", "batlow", "berlin", "bilbao", "broc", "buda", "bukavu", "cork", "davos", "devon", "fes", "grayC", "hawaii", "imola", "lajolla", "lapaz", "lisbon", "nuuk", "oleron", "oslo", "roma", "tofino", "tokyo", "turku", "vanimo", "vik", "actonS", "bamakoS", "batlowS", (:"berlinS",:) "bilbaoS", (:"brocS",:) "budaS", (: "corkS", :) "davosS", "devonS", "grayCS", "hawaiiS", "imolaS", "lajollaS", "lapazS", (:"lisbonS",:) "nuukS", (:"oleronS",:) "osloS", (:"romaS",:) (:"tofinoS",:) "tokyoS", "turkuS", (:"vikS",:) "bamO", "brocO", "corkO", "romaO", "vikO", "batlowK", "batlowW", "inferno", "magma", "plasma", "viridis", "cividis", "twilight", "twilight-blue", "twilight-red", "sawtooth","azimuth", "cube1_0","cubeYF","Linear_L", "bathymetry","bathymetryO","CDOM","chlorophyll","density", "freesurface-red","freesurface-blue","oxygen","phase2","PAR","salinity", "temperature","turbidity","velocity-blue","velocity-green", "vorticity-pink","vorticity-turquoise", "autumn","blackbody","bluered","bone","cold","cool","copper", "cubehelix","earth","greens","greys","hot","hsv", "jet","picnic","portland","rainbow-soft","rainbow","rdbu", "spring","summer","warm","winter","yignbu","yiorrd", "expanded-bone","expanded-copper", "CET-R3","CET-R2","CET-R1","CET-L9","CET-L8","CET-L7","CET-L6","CET-L5", "CET-L4","CET-L3","CET-L2","CET-L1","CET-L19","CET-L18","CET-L17", "CET-L16","CET-L15","CET-L14","CET-L13","CET-L12","CET-L11","CET-L10", "CET-I3","CET-I2","CET-I1","CET-D9","CET-D8","CET-D7","CET-D6","CET-D4", "CET-D3","CET-D2","CET-D1","CET-D1A","CET-D13","CET-D12","CET-D11", "CET-D10","CET-CBTL2","CET-CBTL1","CET-CBTD1","CET-CBTC2","CET-CBTC1", "CET-CBL2","CET-CBL1","CET-CBD1","CET-CBC2","CET-CBC1","CET-C5s", "CET-C5","CET-C4s","CET-C4","CET-C3s","CET-C3","CET-C2s","CET-C2", "CET-C1s","CET-C1", "algae", "amp", "balance", "curl", "deep", "delta", "dense", "diff", "cm-gray", "haline", "ice", "matter", "oxy", "phase", "rain", "solar", "speed", "tarn", "tempo", "thermal", "topo", "turbid", "simpleBlack", "simpleBlue", "simpleGreen", "simpleIce", "simpleWhite" ); declare variable $this:ALIASES as map(xs:string,xs:string) := map { "bluegold": "CET-CBL2", "tampa": "CET-I2", "burn": "CET-L4", "jungle": "CET-L5", "sea": "CET-L6", "blossom": "CET-L7", "oasis": "CET-L11", "sky": "CET-L12", "blood": "CET-L13", "forest": "CET-L14", "deepsea": "CET-L15", "warmth": "CET-L18", "watermelon": "CET-D3" } ; declare variable $this:NAMED-ALIASES as xs:string* := $this:ALIASES=>map:keys(); declare variable $this:WHITES as xs:string* := ( "snow", "seashell", "white", "whitesmoke", "oldlace", "mintcream", "ivory", "ghostwhite", "floralwhite", "cornsilk", "gainsboro", "lemonchiffon" ); (:~ PrettyCols from https://nrennie.github.io/PrettyCols/ CC0 :) declare variable $this:PRETTY-PALETTES as map(xs:string,xs:string*) := map { (: sequential palettes :) "Blues": ( "#436F85", "#4C7D96", "#548BA7", "#6497B1", "#75A2BA", "#86AEC2", "#97B9CB" ), "Purples": ( "#432263", "#502876", "#5D2F89", "#6A359C", "#773BAF", "#8444C0", "#9057C6" ), "Tangerines": ( "#DE7A00", "#F28500", "#FF9B21", "#FFB04F", "#FFC47D", "#FFD6A3", "#FFE1BD" ), "Greens": ( "#416322", "#4E7628", "#5A892F", "#679C35", "#74AF3B", "#80C044", "#8DC657" ), "Pinks": ( "#860A4D", "#9E0C5B", "#B50E68", "#CD1076", "#E51284", "#EE2290", "#F03A9C" ), "Teals": ( "#004C4C", "#006666", "#008080", "#329999", "#66B2B2", "#99CCCC", "#CCE5E5" ), "Yellows": ( "#E6B400", "#E6C700", "#E8D119", "#EBD632", "#F0E066", "#F2E57F", "#F7EFB2" ), "Reds": ( "#B53737", "#BE5151", "#C76B6B", "#D08585", "#D99F9F", "#E3B9B9", "#ECD3D3" ), (: diverging palettes :) "PurpleGreens": ( "#420F75", "#7640A9", "#AD72D6", "#E7A8FB", "#F5F5F5", "#99CE64", "#659A32", "#326812", "#033800" ), "PinkGreens": ( "#7F0038", "#C31E6E", "#EF5FAF", "#FCAADE", "#F5F5F5", "#99CE64", "#659A32", "#326812", "#033800" ), "TangerineBlues": ( "#552000", "#8A4D00", "#C17D17", "#F8B150", "#F5F5F5", "#93C6E1", "#5F93AC", "#2E627A", "#00344A" ), "PurpleTangerines": ( "#420F75", "#7640A9", "#AD72D6", "#E7A8FB", "#F5F5F5", "#F8B150", "#C17D17", "#8A4D00", "#552000"), "PurplePinks": ( "#420F75", "#7640A9", "#AD72D6", "#E7A8FB", "#F5F5F5", "#FCAADE", "#EF5FAF", "#C31E6E", "#7F0038"), "TealGreens": ( "#00393A", "#0A6969", "#2D9C9C", "#6DCFCF", "#F5F5F5", "#99CE64", "#659A32", "#326812", "#033800"), "PurpleYellows": ( "#420F75", "#7640A9", "#AD72D6", "#E7A8FB", "#F5F5F5", "#F2E8C4", "#EED682", "#EAC541", "#E6B400" ), "RedBlues": ( "#B53737", "#C66969", "#D79C9C", "#E8CFCF", "#F5F5F5", "#93C6E1", "#5F93AC", "#2E627A", "#00344A" ), (: qualitative palettes :) "Bold": ( "#6497B1", "#6A359C", "#FFB04F", "#679C35", "#CD1076" ), "Dark": ( "#436F85", "#432263", "#DE7A00", "#416322", "#860A4D" ), "Light": ( "#97B9CB", "#9057C6", "#FFE1BD", "#8DC657", "#F03A9C" ), "Neon": ( "#FF9062", "#FD6598", "#CB64C0", "#3294DD", "#75FB8A", "#d0eb60" ), "Summer": ( "#398DB2", "#D8B31E", "#2C350B", "#829625", "#867112", "#5D761E", "#6293A7", "#3E5A5E", "#AC5C05", "#FFA300", "#A47DB9", "#EC94CA" ), "Autumn": ( "#774762", "#BA6E1D", "#D6BB3B", "#755028", "#F2DD78", "#205F4B", "#913914", "#585854", "#F0A430", "#768048", "#800000", "#1B3A54" ), "Winter": ( "#446C84", "#C0CBDC", "#746E6F", "#C6DCF0", "#596D80", "#B9BFFF", "#A0C4E1", "#897340", "#E1E3E7", "#313C45", "#9BA7B2", "#CAE9F5" ), "Rainbow": ( "#E51E32", "#FF782A", "#FDA805", "#E2CF04", "#B1CA05", "#98C217", "#779815", "#029E77", "#09989C", "#059CCD", "#3F64CE", "#7E2B8E" ), "Beach": ( "#0E7C7B", "#17BEBB", "#D4F4DD", "#D62246", "#4B1D3F" ), "Fun": ( "#134074", "#BFAB25", "#4EA699", "#EFB0A1", "#DF2935" ), "Sea": ( "#86CB92", "#71B48D", "#404E7C", "#3A3559", "#260F26" ), "Bright": ( "#462255", "#FF8811", "#9DD9D2", "#046E8F", "#D44D5C" ), "Relax": ( "#4B3F72", "#CBB3BF", "#FFC857", "#119DA4", "#19647E" ), "Lucent": ( "#E01A4F", "#F15946", "#F9C22E", "#53B3CB", "#7DCFB6" ), "Lively": ( "#413C58", "#D1495B", "#EDAE49", "#00798C", "#003D5B" ), "Joyful": ( "#80A1C1", "#C94277", "#EEE3AB", "#274C77", "#5E8C61" ), (: Pride flag :) "pride": ( "#8e008e", "#400098", "#00c0c0", "#008e00", "#ff0", "#ff8e00", "red", "#ff69b4" ) }; (:~ Werner colours from Werner's Nomenclature of Colours :) declare variable $this:WERNER-PALETTES as map(xs:string,xs:string*) := map { "werner_whites": ( "SnowWhite", "ReddishWhite", "PurplishWhite", "YellowishWhite", "OrangeColouredWhite", "GreenishWhite", "SkimmedMilkWhite", "GreyishWhite" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_greys": ( "AshGrey", "SmokeGrey", "FrenchGrey", "PearlGrey", "YellowishGrey", "BluishGrey", "GreenishGrey", "BlackishGrey" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_blacks": ( "GreyishBlack", "BluishBlack", "GreenishBlack", "PitchOrBrownishBlack", "ReddishBlack", "InkBlack", "VelvetBlack" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_blues": ( "ScotchBlue", "PrussianBlue", "ChinaBlue", "IndigoBlue", "AzureBlue", "UltramarineBlue", "FlaxFlowerBlue", "VerditterBlue", "GreenishBlue", "BerlinBlue", "GreyishBlue" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_purples": ( "BluishLilacPurple", "RedLilacPurple", "BluishPurple", "LavenderPurple", "CampanulaPurple", "ImperialPurple", "AuriculaPurple", "PaleBlackishPurple", "PlumPurple", "VioletPurple", "PansyPurple" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_greens": ( "SiskinGreen", "AsparagusGreen", "CeladineGreen", "AppleGreen", "EmeraldGreen", "BluishGreen", "MountainGreen", "VerdigrisGreen", "LeekGreen", "PistachioGreen", "OilGreen", "GrassGreen", "SapGreen", "OliveGreen", "BlackishGreen", "DuckGreen" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_yellows": ( "CreamYellow", "SiennaYellow", "StrawYellow", "OchreYellow", "PrimroseYellow", "KingsYellow", "GambogeYellow", "LemonYellow", "WineYellow", "SaffronYellow", "SulphurYellow", "WaxYellow", "HoneyYellow", "GallstoneYellow" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_oranges": ( "DutchOrange", "BuffOrange", "OrpimentOrange", "ReddishOrange", "DeepReddishOrange", "BrownishOrange" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_reds": ( "PurplishRed", "VeinousBloodRed", "ChocolateRed", "BrownishRed", "ArterialBloodRed", "ScarletRed", "TileRed", "VermilionRed", "HyacinthRed", "AuroraRed", "FleshRed", "RoseRed", "PeachBlossomRed", "CrimsonRed", "LakeRed", "CarmineRed", "BrownishPurpleRed", "CochinealRed" )!(rgb:named-colour(.)=>rgb:to-string()), "werner_browns": ( "DeepOrangeColouredBrown", "DeepReddishBrown", "UmberBrown", "ChestnutBrown", "YellowishBrown", "WoodBrown", "HairBrown", "BroccoliBrown", "CloveBrown", "LiverBrown", "BlackishBrown" )!(rgb:named-colour(.)=>rgb:to-string()) } ; declare variable $this:PALETTES as map(xs:string,xs:string*) := util:merge-into(( map { "whites": $this:WHITES, "browns": ( "#671905", "#833012", "#974B1A", "#AA6925", "#B6872D", "#BFA73B" ), "olives": ( "#51351F", "#6D5332", "#8C7448", "#AC9561", "#CCB778", "#EEDA91" ), "sunsetland": ( "#51351F", "#846841", "#B6A067", "#EEB46A", "#EA8C42", "#DF631D" ), "october": ( "#695851", "#87837A", "#A4B0A4", "#FED32F", "#FEC52E", "#FCB72C" ), "farhills": ( "#235240", "#426D5A", "#608977", "#80A594", "#A1C1B2", "#C4E0D2" ), "springhills": ( "#636B32", "#778536", "#8E9F41", "#A7B84E", "#C4D263", "#E1EB7A" ), "mountains": ( "#7E507E", "#9C7296", "#BB98AE", "#86963D", "#B0C258", "#E2EB78" ), "wotw": ( "#3D3D1B", "#4F4E20", "#675C2E", "#FC6456", "#EC432E", "#D12615" ), "carnival": ( "#2E294E", "#541388", "#F1E9DA", "#FFD400", "#D90368" ), "leaves": ( "#24400C", "#2C510B", "#396C0D", "#4B8016", "#6BB121", "#8CD01F" ), "lake": ( "#173A3F", "#0C5450", "#10615B", "#166862", "#30817A", "#41918F" ), "high-tea": ( "MiddleBlueGreen", "TeaGreen", "Cream", "MediumChampagne", "DarkByzantium" )!(rgb:named-colour(.)=>rgb:to-string()), "plant-dyes": ( "RedHotPoker", "HawthornLeaf", "BogMyrtle", "Silverweed", "GorseFlower", "ReedFlower", "NettleLeaf", "ColtsFoot", "Acorn", "IrisRoot" )!(rgb:named-colour(.)=>rgb:to-string()) }, $this:WERNER-PALETTES, $this:PRETTY-PALETTES )) ; (: Alt browns: <svg:stop offset="0.0%" stop-color="#7F440E" stop-opacity="1.0000"/> <svg:stop offset="20.0%" stop-color="#8D561F" stop-opacity="1.0000"/> <svg:stop offset="40.0%" stop-color="#A26519" stop-opacity="1.0000"/> <svg:stop offset="60.0%" stop-color="#AD8117" stop-opacity="1.0000"/> <svg:stop offset="80.0%" stop-color="#BE901F" stop-opacity="1.0000"/> <svg:stop offset="100.0%" stop-color="#C8A718" stop-opacity="1.0000"/> :) declare variable $this:NAMED-PALETTES as xs:string* := $this:PALETTES=>map:keys(); declare variable $this:SMALL-GRADIENTS as xs:string* := ( $this:NAMED-PALETTES, "spring", "summer", "autumn", "winter", "bone", "copper", "earth", "electric", "portland", "rainbow-soft", "rainbow", "cold", "cool", "hot", "blackbody", "bluered", "greens", "greys", "hsv", "jet", "mars", "rdbu", "yignbu", "yiorrd", "simpleBlack", "simpleBlue", "simpleGreen", "simpleIce", "simpleWhite" ); declare variable $this:SPLIT-GRADIENTS as xs:string* := ( "bukavu", "fes", "oleron", "topo" ); declare variable $this:CYCLIC-GRADIENTS as xs:string* := ( "bamO", "bathymetryO", "brocO", "corkO", "romaO", "vikO", "CET-CBTC2", "CET-CBTC1", "CET-CBC2", "CET-CBC1"," CET-C5s", "CET-C5", "CET-C4s", "CET-C4", "CET-C3s", "CET-C3", "CET-C2s", "CET-C2", "CET-C1s", "CET-C1", "azimuth", "hsv", "phase", "phase2", "rainbow-soft", "sunlight", "twilight" ); declare variable $this:DIVERGING-GRADIENTS as xs:string* := ( "balance", "bam", "berlin", "broc", "cork", "curl", "delta", "diff", "lisbon", "picnic", "portland", "roma", "tarn", "tofino", "vanimo", "vik", "CET-CBD1", "CET-CBTD1", "CET-D1", "CET-D2", "CET-D3", "CET-D4", "CET-D6", "CET-D7", "CET-D8", "CET-D9", "CET-D10", "CET-D11", "CET-D12", "CET-D13", "CET-D1A", "rdbu", "october", "sunsetland", "PurpleGreens", "PinkGreens", "TangerineBlues", "PurpleTangerines", "PurplePinks", "TealGreens", "PurpleYellows", "RedBlues" ); declare variable $this:LINEAR-GRADIENTS as xs:string* := ( "acton", "bamako", "batlow", "bilbao", "buda", "davos", "devon", "grayC", "hawaii", "imola", "lajolla", "lapaz", "nuuk", "oslo", "tokyo", "turku", "batlowK", "batlowW", "inferno", "magma", "plasma", "viridis", "cividis", "twilight-blue", "twilight-red", "cube1_0", "cubeYF", "Linear_L", "bathymetry", "CDOM", "chlorophyll", "density", "freesurface-red", "freesurface-blue", "oxygen", "PAR", "salinity", "temperature", "turbidity", "velocity-blue", "velocity-green", "vorticity-pink", "vorticity-turquoise", "autumn", "blackbody", "bluered", "bone", "cold", "cool", "copper", "cubehelix", "earth", "greens", "greys", "hot", "jet", "rainbow", "rdbu", "spring", "summer", "warm", "winter", "yignbu", "yiorrd", "expanded-bone", "expanded-copper", "CET-R3", "CET-R2", "CET-R1", "CET-L9", "CET-L8", "CET-L7", "CET-L6", "CET-L5", "CET-L4", "CET-L3", "CET-L2", "CET-L1", "CET-L19", "CET-L18", "CET-L17", "CET-L16", "CET-L15", "CET-L14", "CET-L13", "CET-L12", "CET-L11", "CET-L10", "CET-I3", "CET-I2", "CET-I1", "CET-CBTL2", "CET-CBTL1", "CET-CBL2", "CET-CBL1", "algae", "amp", "deep", "dense", "cm-gray", "haline", "ice", "matter", "rain", "solar", "speed", "tempo", "thermal", "topo", "turbid", "simpleBlack", "simpleBlue", "simpleGreen", "simpleIce", "simpleWhite", "Blues", "Purples", "Tangerines", "Greens", "Pinks", "Teals", "Yellows", "Reds" ); (:~ : starts-darker() : Does this gradient start with darker colours? i.e. dark-to-light : Return false for those that are dark-light-dark or light-dark-light or : scrambled :) declare function this:starts-darker($gradient as xs:string) as xs:boolean { ($gradient = ( "acton", "bamako", "batlow", "buda", "davos", "devon", "hawaii", "imola", "lapaz", "nuuk", "oleron", "oslo", "tokyo", "turku", "actonS", "bamakoS", "batlowS", (:"berlinS",:) "bilbaoS", (:"brocS",:) "budaS", "inferno", "magma", "plasma", "viridis", "cividis", "twilight-blue", "twilight-red", "sawtooth","azimuth", "cube1_0","cubeYF","Linear_L", "bathymetry","bathymetryO","CDOM","chlorophyll","density", "freesurface-red","freesurface-blue","oxygen","PAR","salinity", "temperature","turbidity","velocity-blue","velocity-green", "vorticity-pink","vorticity-turquoise", "autumn","blackbody","bluered","bone","cool","copper", "cubehelix","earth","electric","greens","greys","hot", "jet","picnic","portland","rainbow", "summer","warm","winter","yignbu","yiorrd", "expanded-bone","expanded-copper", "CET-L9","CET-L8","CET-L7","CET-L6","CET-L5", "CET-L4","CET-L3","CET-L2","CET-L1","CET-L18", "CET-L16","CET-L15","CET-L14","CET-L13","CET-L11","CET-L10", "CET-CBTL2","CET-CBTL1", "CET-CBL2","CET-CBL1", "bluegold", "burn", "jungle", "sea", "blossom", "oasis", "blood", "forest", "deepsea", "cm-gray", "haline", "ice", "oxy", "solar", "thermal", "topo", "turbid", "simpleBlack", "browns", "olives", "sunsetland", "october", "farhills", "springhills", "mountains", "wotw", "carnival", "greens", "werner_reds", "werner_blues", "werner_browns", "Blues", "Purples", "Tangerines", "Greens", "Pinks", "Teals", "Yellows", "Reds" )) or (contains($gradient,"-reverse") and not(this:starts-darker(replace($gradient,"-reverse",""))) ) }; (:~ : random-gradient() : Return then name of a random well-known base gradient or its reverse. :) declare function this:random-gradient() as xs:string { let $exclusion := "(^simple)" let $valid-names := ($this:NAMED-GRADIENTS,$this:NAMED-ALIASES,$this:NAMED-PALETTES)[ not(matches(.,$exclusion)) ] let $gradient := rand:select-random($valid-names) return ( if (empty($gradient) or $gradient="") then util:log("Empty gradient? "||string-join($valid-names,"+")) else (), util:assert(not(empty($gradient)), "__empty__"), util:assert($gradient ne "", "__empty string__"), if (rand:flip(50)) then $gradient else $gradient||"-reverse" ) }; (:~ : random-gradient() : Return then name of a random well-known base gradient or its reverse. : : @param $exclusion: regular expression of gradient names to exclude in addition : to simpleXXX :) declare function this:random-gradient($exclusion as xs:string) as xs:string { let $exclusion := "("||$exclusion||")|(^simple)" let $valid-names := ($this:NAMED-GRADIENTS,$this:NAMED-ALIASES,$this:NAMED-PALETTES)[ not(matches(.,$exclusion)) ] let $gradient := rand:select-random($valid-names) return ( if (rand:flip(50)) then $gradient else $gradient||"-reverse" ) }; (:~ : fetch-gradient() : Get the gradient definition of a known named gradient or its alias. : Empty otherwise. :) declare function this:fetch-gradient($gradient as xs:string) as element()? { if ($gradient = $this:NAMED-GRADIENTS) then ( (: SLEAZY HACK ALERT :) let $def := try { doc("../COLOURS/"||$gradient||".xsl")//xsl:param/* } catch * { () } return if (exists($def)) then $def else ( try { doc("../COLOURS/"||$gradient||".svg")//svg:defs/* } catch * { () } ) ) else if ($this:ALIASES($gradient) = $this:NAMED-GRADIENTS) then ( this:fetch-gradient($this:ALIASES($gradient)) ) else ( ) }; (:~ : get-gradient-definition() : Get the gradient definition of the name, allowing for variants for : flips, inversions, samples, etc. :) declare function this:get-gradient-definition($gradient as xs:string) as element()? { let $gradient-pieces := tokenize($gradient, "[·+]") (: middle dot :) let $do-interleave := contains($gradient,"+") let $gradients := for $gradient in $gradient-pieces return ( if ($gradient="random") then ( this:get-gradient-definition(this:random-gradient()) ) else if ($gradient = $this:NAMED-GRADIENTS) then ( this:fetch-gradient($gradient) ) else if ($this:ALIASES($gradient) = $this:NAMED-GRADIENTS) then ( let $def := this:fetch-gradient($this:ALIASES($gradient)) return ( <svg:linearGradient id="{$gradient}">{ $def/(@* except @id), $def/* }</svg:linearGradient> ) ) else if ($gradient = $this:NAMED-PALETTES) then ( this:linear-gradient-definition($gradient, "linear", $this:PALETTES($gradient)) ) else if (matches($gradient,'(-reverse|-random|-flip|-invert|-full|-anti|-outflow|-inflow|-slant|-fade|-[0-9]+)$')) then ( let $root := replace($gradient,'(-reverse|-random|-flip|-invert|-full|-anti|-outflow|-inflow|-slant|-fade|-[0-9]+)$','') let $definition := this:get-gradient-definition($root) let $operation := substring-after($gradient, $root||"-") return switch ($operation) case "random" return this:gradient-random($definition)=>this:gradient-name($gradient) case "reverse" return this:gradient-reverse($definition)=>this:gradient-name($gradient) case "flip" return this:linear-flip($definition)=>this:gradient-name($gradient) case "invert" return this:linear-invert($definition)=>this:gradient-name($gradient) case "full" return this:linear-full($definition)=>this:gradient-name($gradient) case "anti" return this:linear-anti($definition)=>this:gradient-name($gradient) case "outflow" return this:radial-outflow($definition)=>this:gradient-name($gradient) case "inflow" return this:radial-inflow($definition)=>this:gradient-name($gradient) case "slant" return this:linear-slant($definition)=>this:gradient-name($gradient) case "fade" return this:gradient-fade($definition, 0.0, 1.0)=>this:gradient-name($gradient) default (: one of the samples :) return ( let $n := xs:integer($operation) return this:gradient-sample($definition, $n)=>this:gradient-name($gradient) ) ) else if (starts-with($gradient,"black-to-")) then ( let $colour := this:colour(substring-after($gradient,"black-to-")) return <svg:linearGradient id="{this:safe($gradient)}" gradientUnits="objectBoundingBox" spreadMethod="pad" x1="0%" x2="100%" y1="0%" y2="0%"> <svg:stop offset="0%" stop-color="#000000"/> <svg:stop offset="100%" stop-color="{$colour}"/> </svg:linearGradient> ) else if (starts-with($gradient,"white-to-")) then ( let $colour := this:colour(substring-after($gradient,'white-to-')) return <svg:linearGradient id="{this:safe($gradient)}" gradientUnits="objectBoundingBox" spreadMethod="pad" x1="0%" x2="100%" y1="0%" y2="0%"> <svg:stop offset="0%" stop-color="#FFFFFF"/> <svg:stop offset="100%" stop-color="{$colour}"/> </svg:linearGradient> ) else ( (: util:log("gradient="||$gradient||" no definition") :) ) ) return ( if (count($gradients) le 1) then $gradients else if (count($gradients) = count($gradient-pieces)) then ( this:merge($gradient, $gradients, $do-interleave) ) else ( ) ) }; declare function this:gradient($gradient as xs:string) as element()? { this:get-gradient-definition($gradient) }; declare function this:random-colour($gradients as xs:string*) as xs:string { rand:select-random(this:colours($gradients)) }; declare function this:random-colour($gradients as xs:string*, $luminance as xs:double) as xs:string { rand:select-random(this:colours($gradients))=> rgb:rgb()=>cs:rgb-to-xyz()=> tone:luminate($luminance)=> cs:xyz-to-rgb()=>rgb:to-string() }; declare function this:random-colour($gradients as xs:string*, $low-luminance as xs:double, $high-luminance as xs:double) as xs:string { let $colour := rand:select-random(this:colours($gradients)) let $xyz := $colour=>rgb:rgb()=>cs:rgb-to-xyz() let $luminance := tone:luminance($xyz) return ( if (util:twixt($luminance, $low-luminance, $high-luminance)) then $colour else ( $xyz=>tone:luminate(rand:uniform($low-luminance, $high-luminance))=>cs:xyz-to-rgb()=>rgb:to-string() ) ) }; (:~ : colours() : Return the colour strings from the named gradients in order. :) declare function this:colours($gradients as xs:string*) as xs:string* { for $gradient in $gradients return ( let $definition := this:get-gradient-definition($gradient) let $colours := if (empty($definition)) then $gradient else $definition/svg:stop/@stop-color return if (matches($gradient,'-reverse')) then ( reverse($colours) ) else ( $colours ) ) }; (:~ : colours() : Return the colour strings from the named gradients in order, but reluminated : to ensure their luminance is in the given range. :) declare function this:colours( $gradients as xs:string*, $low as xs:double, $high as xs:double ) as xs:string* { let $low := util:clamp-some($low, 0, ()) let $high := util:clamp-some($high, (), 1) let $colours := this:colours($gradients) return ( if ($low <= 0 and $high >= 1) then $colours else ( for $c in $colours let $xyz := rgb:rgb($c)=>cs:rgb-to-xyz() let $luminance := tone:luminance($xyz) return ( if ($luminance < $low) then tone:luminate($xyz, $low)=>cs:xyz-to-rgb()=>rgb:to-string() else if ($luminance > $high) then tone:luminate($xyz, $high)=>cs:xyz-to-rgb()=>rgb:to-string() else $c ) ) ) }; declare function this:gradient-colours($gradients as xs:string*) as xs:string* { this:colours($gradients) }; (:~ : gradient-stops() : Return all the gradient stops, as svg:stop elements, from the given gradient. :) declare function this:gradient-stops($gradient as xs:string) as element(svg:stop )* { this:get-gradient-definition($gradient)/svg:stop }; declare function this:n-gradient-colours($gradient as xs:string) as xs:integer { let $n := count(this:gradient-stops($gradient)) return if ($n=0) then 1 else $n }; declare function this:n-colours($gradient as xs:string) as xs:integer { let $n := count(this:gradient-stops($gradient)) return if ($n=0) then 1 else $n }; (:~ : gradient-points() : Return the points in the gradient as gradient points. This assumes : the stop values are in "rgb(r,g,b)" or hex format. Opacity gives the : alpha channel. : : A gradient point has an offset and a colour point. The set of : gradient points can be used for interpolations and suchlike. : The colour points returned from this are RGBA points. : TODO: select output colour space :) declare function this:gradient-points($gradient as xs:string) as map(xs:string,item()*)* { for $stop in this:gradient-stops($gradient) let $offset := if (ends-with($stop/@offset,"%")) then xs:double(substring-before($stop/@offset,"%")) div 100 else xs:double($stop/@offset) let $alpha := if (empty($stop/@stop-opacity)) then 1 else if (ends-with($stop/@stop-opacity,"%")) then xs:double(substring-before($stop/@stop-opacity,"%")) div 100 else xs:double($stop/@stop-opacity) let $rgba := rgb:rgba(rgb:rgb($stop/@stop-color), $alpha) return stop:stop("rgb", $offset, $rgba) }; declare function this:id($gradient as element()) as xs:string { $gradient/@id }; declare function this:merge( $name as xs:string, $gradients as element()+, $do-interleave as xs:boolean ) as element() { let $kind := typeswitch(head($gradients)) case element(svg:radialGradient) return "outflow" default return "linear" let $max-n := max(for $gradient in $gradients return count($gradient//svg:stop)) let $stops := if ($do-interleave) then ( for $i in 1 to $max-n for $gradient in $gradients return ( ($gradient//svg:stop)[$i] ) ) else ( for $gradient in $gradients return $gradient//svg:stop ) return ( this:gradient-definition($name, $kind, for $stop in $stops return $stop/@stop-opacity, for $stop in $stops return $stop/@stop-color ) ) }; declare function this:sample($colours as xs:string*, $num as xs:integer) as xs:string* { if (empty($colours) or $num <= 1) then () else let $sep := count($colours) div $num for $i in 1 to $num return $colours[1 + ceiling($sep*($i - 1))] }; declare function this:sample($colours as xs:string*, $above as xs:double, $below as xs:double) as xs:string* { this:sample($colours, count($colours), $above, $below) }; declare function this:sample($colours as xs:string*, $num as xs:integer, $above as xs:double, $below as xs:double) as xs:string* { if (empty($colours) or $num <= 1) then () else let $n := count($colours) let $min-n := round($n * $above) cast as xs:integer let $max-n := round($n * $below) cast as xs:integer (: Asymmetry here because we want to allow 0 for $above and 1 for $below :) let $colours := $colours[position() > $min-n and position() <= $max-n] let $sep := count($colours) div $num for $i in 1 to $num return $colours[1 + ceiling($sep*($i - 1))] }; declare function this:sample-stops($stops as element(svg:stop)*, $num as xs:integer) as element(svg:stop)* { if (empty($stops) or $num <= 1) then () else let $sep := count($stops) div $num for $i in 1 to $num return $stops[1 + ceiling($sep*($i - 1))] }; declare function this:circularize($colours as xs:string*) as xs:string* { tail($colours), tail(reverse($colours)) }; declare function this:luminance-range( $colour as xs:string, $n as xs:integer, $delta as xs:double ) as xs:string* { let $rgb := rgb:rgb($colour) let $lum := tone:luminance($rgb) return ( for $l in util:linspace($n, $lum - $delta, $lum, true()) return tone:luminate($rgb, util:clamp($l, 0.0, 1.0))=>rgb:to-string(), $colour, for $l in util:linspace($n, $lum, $lum + $delta)=>tail() return tone:luminate($rgb, util:clamp($l, 0.0, 1.0))=>rgb:to-string() ) }; declare function this:colour($name as xs:string) as xs:string { let $known := $rgb:KNOWN-COLOURS($name) return ( if (empty($known)) then $name else $known ) }; declare function this:ref($name as xs:string) as xs:string { let $known := $rgb:KNOWN-COLOURS($name) return ( if (exists($known)) then $known else if ($name="none") then $name else if (matches($name, "^(rgb|url|#)")) then $name else "url(#"||this:safe($name)||")" ) }; declare function this:safe($name as xs:string) as xs:string { replace(replace($name, "[(),#]", "-"), " ", "") }; (:~ : average-luminance() : Average luminance: a way of determining relative brightness of a set : of colours. :) declare function this:average-luminance($colours as xs:string*) as xs:double { avg( for $colour in $colours return ( tone:luminance(cs:rgb-to-xyz(rgb:rgb($colour))) ) ) }; declare function this:linear-gradient-definition( $name as xs:string, $layout as xs:integer*, (: x1, x2, y1, y2 :) $opacities as xs:double*, (: min, max :) $colours as xs:string* ) as element(svg:linearGradient) { let $n := count($colours) let $x1 := ($layout[1],0)[1] let $x2 := ($layout[2],100)[1] let $y1 := ($layout[3],0)[1] let $y2 := ($layout[4],0)[1] let $start-opacity := ($opacities[1],1)[1] let $end-opacity := ($opacities[2],1)[1] return if ($start-opacity=$end-opacity) then ( <svg:linearGradient id="{$name}" gradientUnits="objectBoundingBox" spreadMethod="pad" x1="{$x1}%" x2="{$x2}%" y1="{$y1}%" y2="{$y2}%"> { <svg:stop offset="0.00%" stop-color="{$colours[1]}" stop-opacity="{$start-opacity}"/> , if ($n > 100) then ( for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) (: let $_ := xdmp:log($i||" "||$colour||" "||$offset) :) return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$start-opacity}"/>, if (exists($colours[$i + 1])) then <svg:stop offset="{$offset}%" stop-color="{$colours[$i + 1]}" stop-opacity="{$start-opacity}"/> else () ) ) else ( for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) where $i > 1 and $i < $n return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$start-opacity}"/> ) ) , <svg:stop offset="100.00%" stop-color="{$colours[last()]}" stop-opacity="{$start-opacity}"/> } </svg:linearGradient> ) else ( <svg:linearGradient id="{$name}" gradientUnits="objectBoundingBox" spreadMethod="pad" x1="{$x1}%" x2="{$x2}%" y1="{$y1}%" y2="{$y2}%"> { <svg:stop offset="0.00%" stop-color="{$colours[1]}" stop-opacity="{$start-opacity}"/> , if ($n > 100) then ( let $delta := ($end-opacity - $start-opacity) div $n for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$opacity}"/>, if (exists($colours[$i + 1])) then <svg:stop offset="{$offset}%" stop-color="{$colours[$i + 1]}" stop-opacity="{$opacity}"/> else () ) ) else ( let $delta := ($end-opacity - $start-opacity) div $n for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) where $i > 1 and $i < $n return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$opacity}"/> ) ) , <svg:stop offset="100.00%" stop-color="{$colours[last()]}" stop-opacity="{$end-opacity}"/> } </svg:linearGradient> ) }; declare function this:linear-standard-layout($kind as xs:string) as xs:integer* { switch ($kind) case "reverse" return (100,0,0,0) case "flip" return (0,0,0,100) case "invert" return (0,0,100,0) case "full" return (0,100,0,100) case "anti" return (100,0,100,0) case "slant" return (0,50,0,90) default return (0,100,0,0) }; declare function this:linear-gradient-definition( $name as xs:string, $kind as xs:string, $colours as xs:string* ) as element(svg:linearGradient) { this:linear-gradient-definition($name, this:linear-standard-layout($kind), (1,1), $colours) }; declare function this:radial-gradient-definition( $name as xs:string, $layout as xs:integer*, (: r, cx, cy :) $opacities as xs:double*, $colours as xs:string* ) as element(svg:radialGradient) { let $n := count($colours) let $r := ($layout[1],50)[1] let $cx := ($layout[2],50)[1] let $cy := ($layout[3],$cx)[1] let $start-opacity := ($opacities[1],1)[1] let $end-opacity := ($opacities[2],1)[1] return if ($start-opacity=$end-opacity) then ( <svg:radialGradient id="{$name}" gradientUnits="objectBoundingBox" spreadMethod="pad" cx="{$cx}%" cy="{$cy}%" r="{$r}%"> { <svg:stop offset="0.00%" stop-color="{$colours[1]}" stop-opacity="{$start-opacity}"/> , if ($n > 5) then ( for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$start-opacity}"/>, if ($n > 100 and exists($colours[$i + 1])) then <svg:stop offset="{$offset}%" stop-color="{$colours[$i + 1]}" stop-opacity="{$start-opacity}"/> else () ) ) else ( for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) where $i > 1 and $i < $n return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$start-opacity}"/> ) ) , <svg:stop offset="100.00%" stop-color="{$colours[last()]}" stop-opacity="{$start-opacity}"/> } </svg:radialGradient> ) else ( <svg:radialGradient id="{$name}" gradientUnits="objectBoundingBox" spreadMethod="pad" cx="{$cx}%" cy="{$cy}%" r="{$r}%"> { <svg:stop offset="0.00%" stop-color="{$colours[1]}" stop-opacity="{$start-opacity}"/> , if ($n > 5) then ( let $delta := ($end-opacity - $start-opacity) div $n for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$opacity}"/>, if ($n > 100 and exists($colours[$i + 1])) then <svg:stop offset="{$offset}%" stop-color="{$colours[$i + 1]}" stop-opacity="{$opacity}"/> else () ) ) else ( let $delta := ($end-opacity - $start-opacity) div $n for $colour at $i in $colours let $offset := round(100* 100*$i div $n) div 100 (: percent to 2 decimals :) let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) where $i > 1 and $i < $n return ( <svg:stop offset="{$offset}%" stop-color="{$colour}" stop-opacity="{$opacity}"/> ) ) , <svg:stop offset="100.00%" stop-color="{$colours[last()]}" stop-opacity="{$end-opacity}"/> } </svg:radialGradient> ) }; declare function this:radial-standard-layout($kind as xs:string) as xs:integer* { if ($kind="stargate") then (50,50,80) else (50,50,50) }; declare function this:radial-colours($kind as xs:string, $colours as xs:string*) as xs:string* { if ($kind="inflow") then reverse($colours) else $colours }; declare function this:radial-gradient-definition( $name as xs:string, $kind as xs:string, $colours as xs:string* ) as element(svg:radialGradient) { this:radial-gradient-definition($name, this:radial-standard-layout($kind), (1,1), this:radial-colours($kind, $colours)) }; declare function this:gradient-definition( $name as xs:string, $kind as xs:string, $opacities as xs:double*, $colours as xs:string* ) as element() { if ($kind=("inflow","outflow","stargate")) then ( this:radial-gradient-definition($name, this:radial-standard-layout($kind), $opacities, this:radial-colours($kind, $colours)) ) else ( this:linear-gradient-definition($name, this:linear-standard-layout($kind), $opacities, $colours) ) }; declare function this:gradient-definition( $name as xs:string, $kind as xs:string, $colours as xs:string* ) as element() { this:gradient-definition($name, $kind, (1,1), $colours) }; declare function this:is-radial($def as element()) as xs:boolean { local-name($def)="radialGradient" }; (:======================================================================: : Gradient manipulations :======================================================================:) declare function this:gradient-name( $base-gradient as element(), $name as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-name($base-gradient, $name) else this:linear-name($base-gradient, $name) }; declare function this:gradient-reverse( $base-gradient as element() ) as element() { if (this:is-radial($base-gradient)) then this:radial-reverse($base-gradient) else this:linear-reverse($base-gradient) }; declare %art:deprecated function this:gradient-reverse( $name as xs:string, $base-gradient as element() ) as element() { if (this:is-radial($base-gradient)) then this:radial-reverse($base-gradient)=>this:radial-name($name) else this:linear-reverse($base-gradient)=>this:linear-name($name) }; declare function this:gradient-random( $base-gradient as element() ) as element() { if (this:is-radial($base-gradient)) then this:radial-random($base-gradient) else this:linear-random($base-gradient) }; declare %art:deprecated function this:gradient-random( $name as xs:string, $base-gradient as element() ) as element() { if (this:is-radial($base-gradient)) then this:radial-random($base-gradient)=>this:radial-name($name) else this:linear-random($base-gradient)=>this:linear-name($name) }; declare function this:gradient-sample( $base-gradient as element(), $num as xs:integer ) as element() { if (this:is-radial($base-gradient)) then this:radial-sample($base-gradient, $num) else this:linear-sample($base-gradient, $num) }; declare %art:deprecated function this:gradient-sample( $name as xs:string, $base-gradient as element(), $num as xs:integer ) as element() { if (this:is-radial($base-gradient)) then this:radial-sample($base-gradient, $num)=>this:radial-name($name) else this:linear-sample($base-gradient, $num)=>this:linear-name($name) }; declare function this:gradient-fade( $base-gradient as element(), $start-opacity as xs:double, $end-opacity as xs:double ) as element() { if (this:is-radial($base-gradient)) then this:radial-fade($base-gradient, $start-opacity, $end-opacity) else this:linear-fade($base-gradient, $start-opacity, $end-opacity) }; declare %art:deprecated function this:gradient-fade( $name as xs:string, $base-gradient as element(), $start-opacity as xs:double, $end-opacity as xs:double ) as element() { if (this:is-radial($base-gradient)) then this:radial-fade($base-gradient, $start-opacity, $end-opacity)=>this:radial-name($name) else this:linear-fade($base-gradient, $start-opacity, $end-opacity)=>this:linear-name($name) }; declare function this:gradient-transform( $base-gradient as element(), $transform as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-transform($base-gradient, $transform) else this:linear-transform($base-gradient, $transform) }; declare %art:deprecated function this:gradient-transform( $name as xs:string, $base-gradient as element(), $transform as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-transform($base-gradient, $transform)=>this:radial-name($name) else this:linear-transform($base-gradient, $transform)=>this:linear-name($name) }; declare function this:gradient-units( $base-gradient as element(), $objectBoundingBox as xs:boolean ) as element() { if (this:is-radial($base-gradient)) then this:radial-units($base-gradient, $objectBoundingBox) else this:linear-units($base-gradient, $objectBoundingBox) }; declare %art:deprecated function this:gradient-units( $name as xs:string, $base-gradient as element(), $objectBoundingBox as xs:boolean ) as element() { if (this:is-radial($base-gradient)) then this:radial-units($base-gradient, $objectBoundingBox)=>this:radial-name($name) else this:linear-units($base-gradient, $objectBoundingBox)=>this:linear-name($name) }; declare function this:gradient-spread( $base-gradient as element(), $method as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-spread($base-gradient, $method) else this:linear-spread($base-gradient, $method) }; declare %art:deprecated function this:gradient-spread( $name as xs:string, $base-gradient as element(), $method as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-spread($base-gradient, $method)=>this:radial-name($name) else this:linear-spread($base-gradient, $method)=>this:linear-name($name) }; declare function this:gradient-layout( $base-gradient as element(), $layout as xs:integer* ) as element() { if (this:is-radial($base-gradient)) then this:radial-layout($base-gradient, $layout) else this:linear-layout($base-gradient, $layout) }; declare %art:deprecated function this:gradient-layout( $name as xs:string, $base-gradient as element(), $layout as xs:integer* ) as element() { if (this:is-radial($base-gradient)) then this:radial-layout($base-gradient, $layout)=>this:radial-name($name) else this:linear-layout($base-gradient, $layout)=>this:linear-name($name) }; declare function this:gradient-interleave( $base-gradient as element(), $interleave as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-interleave($base-gradient, $interleave) else this:linear-interleave($base-gradient, $interleave) }; declare %art:deprecated function this:gradient-interleave( $name as xs:string, $base-gradient as element(), $interleave as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-interleave($base-gradient, $interleave)=>this:radial-name($name) else this:linear-interleave($base-gradient, $interleave)=>this:linear-name($name) }; declare function this:gradient-opacity( $base-gradient as element(), $opacity as function(xs:integer) as xs:double ) as element() { if (this:is-radial($base-gradient)) then this:radial-opacity($base-gradient, $opacity) else this:linear-opacity($base-gradient, $opacity) }; declare %art:deprecated function this:gradient-opacity( $name as xs:string, $base-gradient as element(), $opacity as function(xs:integer) as xs:double ) as element() { if (this:is-radial($base-gradient)) then this:radial-opacity($base-gradient, $opacity)=>this:radial-name($name) else this:linear-opacity($base-gradient, $opacity)=>this:linear-name($name) }; declare function this:gradient-colour( $base-gradient as element(), $colour as function(xs:integer) as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-colour($base-gradient, $colour) else this:linear-colour($base-gradient, $colour) }; declare %art:deprecated function this:gradient-colour( $name as xs:string, $base-gradient as element(), $colour as function(xs:integer) as xs:string ) as element() { if (this:is-radial($base-gradient)) then this:radial-colour($base-gradient, $colour)=>this:radial-name($name) else this:linear-colour($base-gradient, $colour)=>this:linear-name($name) }; declare function this:gradient-ref( $name as xs:string, $base-gradient-name as xs:string ) as element() { let $def := this:gradient($base-gradient-name) return ( if (exists($def) and this:is-radial($def)) then this:radial-ref($name, $base-gradient-name) else this:linear-ref($name, $base-gradient-name) ) }; declare function this:linear-name( $base-gradient as element(svg:linearGradient), $name as xs:string ) as element(svg:linearGradient) { <svg:linearGradient id="{this:safe($name)}">{ $base-gradient/(@* except @id), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-reverse( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$base-gradient/@x2}" x2="{$base-gradient/@x1}" y1="{$base-gradient/@y2}" y2="{$base-gradient/@y1}">{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-flip( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$base-gradient/@y1}" x2="{$base-gradient/@y2}" y1="{$base-gradient/@x1}" y2="{$base-gradient/@x2}" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-invert( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$base-gradient/@y1}" x2="{$base-gradient/@y2}" y1="{$base-gradient/@x2}" y2="{$base-gradient/@x1}" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-full( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient y1="{$base-gradient/@x1}" y2="{$base-gradient/@x2}" >{ $base-gradient/(@* except (@y1|@y2)), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-anti( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$base-gradient/@x2}" x2="{$base-gradient/@x1}" y1="{$base-gradient/@x2}" y2="{$base-gradient/@x1}" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-slant( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { <svg:linearGradient x1="0%" x2="50%" y1="0%" y2="90%" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-adjust( $base-gradient as element(svg:linearGradient), $xys as xs:integer* (: x1, y1, x2, y2 :) ) as element(svg:linearGradient) { <svg:linearGradient x1="{$xys[1]}%" x2="{$xys[3]}%" y1="{$xys[2]}%" y2="{$xys[4]}%" >{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-random( $base-gradient as element(svg:linearGradient) ) as element(svg:linearGradient) { let $stops := rand:shuffle($base-gradient/svg:stop) let $percentages := util:linspace(count($stops), 0, 100)!util:decimal(., 2) return <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $stops return ( <svg:stop offset="{$percentages[$i]}%">{ $stop/(@* except @offset) }</svg:stop> ) }</svg:linearGradient> }; declare function this:linear-interleave( $base-gradient as element(svg:linearGradient), $interleave as xs:string ) as element(svg:linearGradient) { <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop return ( if ($i mod 2 = 0) then $stop else ( <svg:stop stop-color="{$interleave}">{ $stop/(@* except @stop-color) }</svg:stop> ) ) }</svg:linearGradient> }; declare function this:linear-opacity( $base-gradient as element(svg:linearGradient), $opacity as function(xs:integer) as xs:double ) as element(svg:linearGradient) { <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $stop-opacity := util:decimal(util:clamp($opacity($i),0,1),3) return ( <svg:stop stop-opacity="{$stop-opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:linearGradient> }; declare function this:linear-colour( $base-gradient as element(svg:linearGradient), $colour as function(xs:integer) as xs:string ) as element(svg:linearGradient) { <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $stop-colour := $colour($i) return ( <svg:stop stop-color="{$stop-colour}">{ $stop/(@* except @stop-color) }</svg:stop> ) }</svg:linearGradient> }; declare function this:linear-sample( $base-gradient as element(svg:linearGradient), $num as xs:integer ) as element(svg:linearGradient) { if ($num <= 1) then ( <svg:linearGradient>{ $base-gradient/@*, $base-gradient/* }</svg:linearGradient> ) else ( let $stops := $base-gradient/svg:stop let $percentages := util:linspace($num, 0, 100)!util:decimal(., 2) let $sep := count($stops) div $num return if (count($stops) <= $num) then ( <svg:linearGradient>{ $base-gradient/@*, $base-gradient/* }</svg:linearGradient> ) else ( <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $i in 1 to $num let $stop := $stops[1 + ceiling($sep*($i - 1))] return ( <svg:stop offset="{$percentages[$i]}%">{ $stop/(@* except @offset) }</svg:stop> ) }</svg:linearGradient> ) ) }; declare function this:linear-fade( $base-gradient as element(svg:linearGradient), $start-opacity as xs:double, $end-opacity as xs:double ) as element(svg:linearGradient) { let $n := count($base-gradient/svg:stop) let $delta := ($end-opacity - $start-opacity) div ($n - 1) return <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) return ( <svg:stop stop-opacity="{$opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:linearGradient> }; declare function this:linear-symmetric-fade( $base-gradient as element(svg:linearGradient), $start-opacity as xs:double, $peak-opacity as xs:double, $end-opacity as xs:double ) as element(svg:linearGradient) { let $n := count($base-gradient/svg:stop) let $half := $n idiv 2 let $delta1 := ($peak-opacity - $start-opacity) div ($n idiv 2) let $delta2 := ($end-opacity - $peak-opacity) div (($n + 1) idiv 2) return <svg:linearGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $opacity := if ($i <= $half) then util:decimal($start-opacity + $delta1*($i - 1), 2) else util:decimal($peak-opacity + $delta2*($i - $half - 1), 2) return ( <svg:stop stop-opacity="{$opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:linearGradient> }; declare function this:linear-transform( $base-gradient as element(svg:linearGradient), $transform as xs:string ) as element(svg:linearGradient) { <svg:linearGradient gradientTransform="{$transform}">{ $base-gradient/(@* except @gradientTransform), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-units( $base-gradient as element(svg:linearGradient), $objectBoundingBox as xs:boolean ) as element(svg:linearGradient) { let $val := if ($objectBoundingBox) then "objectBoundingBox" else "userSpaceOnUse" return <svg:linearGradient gradientUnits="{$val}">{ $base-gradient/(@* except @gradientUnits), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-spread( $base-gradient as element(svg:linearGradient), $method as xs:string ) as element(svg:linearGradient) { <svg:linearGradient spreadMethod="{$method}">{ $base-gradient/(@* except @spreadMethod), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-layout( $base-gradient as element(svg:linearGradient), $layout as xs:integer* ) as element(svg:linearGradient) { let $x1 := ($layout[1],0)[1] let $x2 := ($layout[2],100)[1] let $y1 := ($layout[3],0)[1] let $y2 := ($layout[4],0)[1] return <svg:linearGradient x1="{$x1}%" x2="{$x2}%" y1="{$y1}%" y2="{$y2}%">{ $base-gradient/(@* except (@x1|@x2|@y1|@y2)), $base-gradient/* }</svg:linearGradient> }; declare function this:linear-ref( $name as xs:string, $base-gradient-name as xs:string ) as element(svg:linearGradient) { <svg:linearGradient id="{this:safe($name)}" xlink:href="#{$base-gradient-name}"/> }; declare function this:radial-name( $base-gradient as element(svg:radialGradient), $name as xs:string ) as element(svg:radialGradient) { <svg:radialGradient id="{this:safe($name)}">{ $base-gradient/(@* except @id), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-outflow( $base-gradient as element(svg:linearGradient) ) as element(svg:radialGradient) { <svg:radialGradient cx="50%" cy="50%" r="50%">{ $base-gradient/(@* except (@r|@cx|@cy)), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-inflow( $base-gradient as element(svg:linearGradient) ) as element(svg:radialGradient) { let $offsets := reverse( for $offset in $base-gradient/svg:stop/@offset return if (contains($offset,"%")) then util:decimal(100.0 - number(substring-before($offset,"%")), 2) else util:decimal(100.0 - number($offset), 2) ) let $colours := reverse($base-gradient/svg:stop/@stop-color) let $opacities := reverse($base-gradient/svg:stop/@stop-opacity) return ( <svg:radialGradient cx="50%" cy="50%" r="50%">{ $base-gradient/(@* except (@r|@cx|@cy)), $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop return ( <svg:stop offset="{$offsets[$i]}%" stop-color="{$colours[$i]}" stop-opacity="{$opacities[$i]}"/> ) }</svg:radialGradient> ) }; declare function this:radial-reverse( $base-gradient as element(svg:radialGradient) ) as element(svg:radialGradient) { let $offsets := reverse( for $offset in $base-gradient/svg:stop/@offset return if (contains($offset,"%")) then util:decimal(100.0 - number(substring-before($offset,"%")), 2) else util:decimal(100.0 - number($offset), 2) ) let $colours := reverse($base-gradient/svg:stop/@stop-color) let $opacities := reverse($base-gradient/svg:stop/@stop-opacity) return ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop return ( <svg:stop offset="{$offsets[$i]}%" stop-color="{$colours[$i]}" stop-opacity="{$opacities[$i]}"/> ) }</svg:radialGradient> ) }; declare function this:radial-random( $base-gradient as element(svg:radialGradient) ) as element(svg:radialGradient) { let $stops := rand:shuffle($base-gradient/svg:stop) let $percentages := util:linspace(count($stops), 0, 100)!util:decimal(., 2) return ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $stops return ( <svg:stop offset="{$percentages[$i]}%">{ $stop/(@* except @offset) }</svg:stop> ) }</svg:radialGradient> ) }; declare function this:radial-interleave( $base-gradient as element(svg:radialGradient), $interleave as xs:string ) as element(svg:radialGradient) { <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop return ( if ($i mod 2 = 0) then $stop else ( <svg:stop stop-color="{$interleave}">{ $stop/(@* except @stop-color) }</svg:stop> ) ) }</svg:radialGradient> }; declare function this:radial-opacity( $base-gradient as element(svg:radialGradient), $opacity as function(xs:integer) as xs:double ) as element(svg:radialGradient) { <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $stop-opacity := util:decimal(util:clamp($opacity($i),0,1),3) return ( <svg:stop stop-opacity="{$stop-opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:radialGradient> }; declare function this:radial-colour( $base-gradient as element(svg:radialGradient), $colour as function(xs:integer) as xs:string ) as element(svg:radialGradient) { <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $stop-colour := $colour($i) return ( <svg:stop stop-color="{$stop-colour}">{ $stop/(@* except @stop-color) }</svg:stop> ) }</svg:radialGradient> }; declare function this:radial-sample( $base-gradient as element(svg:radialGradient), $num as xs:integer ) as element(svg:radialGradient) { if ($num <= 1) then ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/* }</svg:radialGradient> ) else ( let $stops := $base-gradient/svg:stop let $percentages := util:linspace($num, 0, 100)!util:decimal(., 2) let $sep := count($stops) div $num return if (count($stops) <= $num) then ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/* }</svg:radialGradient> ) else ( <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $i in 1 to $num let $stop := $stops[1 + ceiling($sep*($i - 1))] return ( <svg:stop offset="{$percentages[$i]}%">{$stop/(@* except @offset)}</svg:stop> ) }</svg:radialGradient> ) ) }; declare function this:radial-fade( $base-gradient as element(svg:radialGradient), $start-opacity as xs:double, $end-opacity as xs:double ) as element(svg:radialGradient) { let $n := count($base-gradient/svg:stop) let $delta := ($end-opacity - $start-opacity) div $n return <svg:radialGradient>{ $base-gradient/@*, $base-gradient/(* except svg:stop), for $stop at $i in $base-gradient/svg:stop let $opacity := util:decimal($start-opacity + $delta*($i - 1), 2) return ( <svg:stop stop-opacity="{$opacity}">{ $stop/(@* except @stop-opacity) }</svg:stop> ) }</svg:radialGradient> }; declare function this:radial-units( $base-gradient as element(svg:radialGradient), $objectBoundingBox as xs:boolean ) as element(svg:radialGradient) { let $val := if ($objectBoundingBox) then "objectBoundingBox" else "userSpaceOnUse" return <svg:radialGradient gradientUnits="{$val}">{ $base-gradient/(@* except @gradientUnits), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-center( $base-gradient as element(svg:radialGradient), $cx-percent as xs:integer, $cy-percent as xs:integer ) as element(svg:radialGradient) { <svg:radialGradient cx="{$cx-percent}%" cy="{$cy-percent}%">{ $base-gradient/(@* except (@cx|@cy)), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-focus( $base-gradient as element(svg:radialGradient), $fx-percent as xs:integer, $fy-percent as xs:integer ) as element(svg:radialGradient) { <svg:radialGradient fx="{$fx-percent}%" fy="{$fy-percent}%">{ $base-gradient/(@* except (@fx|@fy)), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-radius( $base-gradient as element(svg:radialGradient), $r-percent as xs:integer ) as element(svg:radialGradient) { <svg:radialGradient r="{$r-percent}%">{ $base-gradient/(@* except (@r)), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-circle( $base-gradient as element(svg:radialGradient), $radial-circle as map(xs:string,item()*) (: all as percents :) ) as element(svg:radialGradient) { let $r := ellipse:radius($radial-circle) let $center := ellipse:center($radial-circle) let $cx := point:x($center) let $cy := point:y($center) return <svg:radialGradient cx="{$cx}%" cy="{$cy}%" r="{$r}%">{ $base-gradient/(@* except (@r|@cx|@cy)), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-transform( $base-gradient as element(svg:radialGradient), $transform as xs:string ) as element(svg:radialGradient) { <svg:radialGradient gradientTransform="{$transform}">{ $base-gradient/(@* except @gradientTransform), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-spread( $base-gradient as element(svg:radialGradient), $method as xs:string ) as element(svg:radialGradient) { <svg:radialGradient spreadMethod="{$method}">{ $base-gradient/(@* except @spreadMethod), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-layout( $base-gradient as element(svg:radialGradient), $layout as xs:integer* (: r, cx, cy :) ) as element(svg:radialGradient) { let $r := ($layout[1],50)[1] let $cx := ($layout[2],50)[1] let $cy := ($layout[3],$cx)[1] return <svg:radialGradient cx="{$cx}%" cy="{$cy}%" r="{$r}%">{ $base-gradient/(@* except (@r|@cx|@cy)), $base-gradient/* }</svg:radialGradient> }; declare function this:radial-ref( $name as xs:string, $base-gradient-name as xs:string ) as element(svg:radialGradient) { <svg:radialGradient id="{this:safe($name)}" xlink:href="#{$base-gradient-name}"/> }; declare function this:expand-evenly( $num-stops as xs:integer, $base-rgb-str as xs:string*, $do-spline as xs:boolean ) as xs:string* { (: To end up w/ 512 stops, we need to add n interpolations to each edge : we have ns stops already, so: : (ns - 1)*n + ns = 512 : n = (512 - ns)/(ns - 1) : We can only interpolate an integer number of points so that : leaves us a residue which we use as a probability of adding another point :) let $ns := count($base-rgb-str) let $n := ($num-stops - $ns) idiv ($ns - 1) let $residue := 100 * ($ns - 1) * ((($num-stops - $ns) div ($ns - 1)) - $n) let $base-rgb := $base-rgb-str!rgb:rgb(.) let $base-hsl := $base-rgb!cs:rgb-to-hsluv(.) let $expanded-hsl := ( $base-hsl[1], let $edges := if ($do-spline) then ( spline:spline($base-hsl)=>path:edges() ) else ( edge:to-edges($base-hsl) ) for $edge in $edges let $n-interp := if (rand:flip($residue)) then $n + 1 else $n let $interp := tail(edge:interpolate($n-interp, $edge)) return $interp ) let $expanded-rgb := $expanded-hsl!cs:hsluv-to-rgb(.) let $expanded-rgb-str := $expanded-rgb!rgb:to-string(.) return $expanded-rgb-str }; (: Interpolate in a way to preserve relative scaling :) declare function this:expand-scaled( $num-stops as xs:integer, $base-rgb-str as xs:string*, $base-percents as xs:double*, $do-spline as xs:boolean ) as xs:string* { let $ns := count($base-rgb-str) let $edge-fractions := for $percent at $i in tail($base-percents) return ($percent - $base-percents[$i]) div 100.0 (: To end up w/ 512 stops, we need to add 512 - ns total : allocated in accordance with the percentage differences for each : edge : Use $num-stops - 1 because the percentages end up including the end : points so that works better : We can only interpolate an integer number of points so that : leaves us a residue which we use as a probability of adding another point :) let $edge-ns := let $total := $num-stops - 1 (: $ns :) for $fraction in $edge-fractions return round($total * $fraction) cast as xs:integer let $residue := 100 * (($num-stops - (: $ns :) 1) - sum($edge-ns)) div ($ns - 1) let $base-rgb := $base-rgb-str!rgb:rgb(.) let $base-hsl := $base-rgb!cs:rgb-to-hsluv(.) let $expanded-hsl := ( $base-hsl[1], let $edges := if ($do-spline) then ( spline:spline($base-hsl)=>path:edges() ) else ( edge:to-edges($base-hsl) ) for $edge at $i in $edges let $n-interp := if (rand:flip($residue)) then $edge-ns[$i] + 1 else $edge-ns[$i] let $interp := tail(edge:interpolate($n-interp, $edge)) return $interp ) let $expanded-rgb := $expanded-hsl!cs:hsluv-to-rgb(.) let $expanded-rgb-str := $expanded-rgb!rgb:to-string(.) return $expanded-rgb-str }; declare function this:expand-gradient( $num-stops as xs:integer, $base-gradient as xs:string, $do-even as xs:boolean, $do-spline as xs:boolean ) as xs:string* { let $base-rgb-str := this:gradient-colours($base-gradient) let $base-percents := for $offset in this:gradient-stops($base-gradient)/@offset return xs:double(substring-before(string($offset),"%")) return if ($do-even) then this:expand-evenly($num-stops, $base-rgb-str, $do-spline) else this:expand-scaled($num-stops, $base-rgb-str, $base-percents, $do-spline) };