diff --git a/scripts/color.nu b/scripts/color.nu new file mode 100644 index 0000000000000000000000000000000000000000..b97c134b39df55108041c3f990caafbe6fca3284 --- /dev/null +++ b/scripts/color.nu @@ -0,0 +1,133 @@ +export const WHITE = { r: 1.0, g: 1.0, b: 1.0 } +export const BLACK = { r: 0.0, g: 0.0, b: 0.0 } +export const RED = { r: 1.0, g: 0.0, b: 0.0 } +export const GREEN = { r: 0.0, g: 1.0, b: 0.0 } +export const BLUE = { r: 0.0, g: 0.0, b: 1.0 } + +def "error throw" [err: record<err: string, label: string, span: record<start: int, end: int>>] { + error make { + msg: $"(ansi red_bold)($err.err)(ansi reset)", + label: { + text: $err.label, + span: $err.span, + }, + } +} + +export def "color from-floats" [ + r: float, + g: float, + b: float +]: nothing -> record<r: float, g: float, b: float> { + if $r < 0.0 or $r > 1.0 { + error throw { + err: "invalid RGB channel", + label: $"should be between 0 and 1, found ($r)", + span: (metadata $r).span, + } + } + if $g < 0.0 or $g > 1.0 { + error throw { + err: "invalid RGB channel", + label: $"should be between 0 and 1, found ($g)", + span: (metadata $g).span, + } + } + if $b < 0.0 or $b > 1.0 { + error throw { + err: "invalid RGB channel", + label: $"should be between 0 and 1, found ($b)", + span: (metadata $b).span, + } + } + + { r: $r, g: $g, b: $b } +} + +export def "color from-ints" [ + r: int, + g: int, + b: int +]: nothing -> record<r: float, g: float, b: float> { + if $r < 0 or $r > 255 { + error throw { + err: "invalid RGB channel", + label: $"should be between 0 and 255, found ($r)", + span: (metadata $r).span, + } + } + if $g < 0 or $g > 255 { + error throw { + err: "invalid RGB channel", + label: $"should be between 0 and 255, found ($g)", + span: (metadata $g).span, + } + } + if $b < 0 or $b > 255 { + error throw { + err: "invalid RGB channel", + label: $"should be between 0 and 255, found ($b)", + span: (metadata $b).span, + } + } + + { r: ($r / 255 | into float), g: ($g / 255 | into float), b: ($b / 255 | into float) } +} + +def try-string-to-int []: string -> int { + try { + $"0x($in)" | into int + } catch { + get debug | parse --regex 'CantConvert { to_type: "(?<to>.*)", from_type: "(?<from>.*)", span: Span { (?<span>.*) }, help: Some\("(?<help>.*)"\) }' | into record | error make --unspanned { msg: ($in.help | str replace --all '\"' '"') } + } +} + +export def "color from-string" [s: string]: nothing -> record<r: float, g: float, b: float> { + let res = $s + | parse --regex '^#(?<r>..)(?<g>..)(?<b>..)$' + | into record + + if $res == {} { + error throw { + err: "invalid HEX color format", + label: $"format should be '#RRGGBB', found ($s)", + span: (metadata $s).span, + } + } + + { + r: ($res.r | try-string-to-int | $in / 255), + g: ($res.g | try-string-to-int | $in / 255), + b: ($res.b | try-string-to-int | $in / 255), + } +} + +export def "color mix" [ + c1: record<r: float, g: float, b: float>, + c2: record<r: float, g: float, b: float>, + c: float, +]: nothing -> record<r: float, g: float, b: float> { + { + r: ($c * $c1.r + (1 - $c) * $c2.r), + g: ($c * $c1.g + (1 - $c) * $c2.g), + b: ($c * $c1.b + (1 - $c) * $c2.b), + } +} + +def float-to-u8-hex []: float -> string { + $in * 255 + | math round --precision 0 + | into int + | fmt + | get lowerhex + | parse "0x{n}" + | into record + | get n + | into string + | fill --alignment "right" --character '0' --width 2 +} + +export def "color to-hex" []: record<r: float, g: float, b: float> -> string { + $"#($in.r | float-to-u8-hex)($in.g | float-to-u8-hex)($in.b | float-to-u8-hex)" +} + diff --git a/scripts/inbreeding/plot.nu b/scripts/inbreeding/plot.nu index 17807e9f245700e2de7f050403f82f81be4e4005..4a7063538972bf139f0ed499cb926df4fada4a21 100644 --- a/scripts/inbreeding/plot.nu +++ b/scripts/inbreeding/plot.nu @@ -3,6 +3,7 @@ use std repeat use ../plot.nu gplt +use ../color.nu * def "parse strategy" []: string -> record<type: string> { let s = $in @@ -24,6 +25,21 @@ def "parse strategy" []: string -> record<type: string> { } } +def get-color []: int -> string { + match $in { + 10 => "#d62728", + 9 => "#ff7f0e", + 8 => "#bcbd22", + 7 => "#1f77b4", + 6 => "#9467bd", + 5 => "#2ca02c", + 4 => "#17becf", + 3 => "#8c564b", + 2 => "#e377c2", + _ => "#7f7f7f", + } +} + export def main [data: path, --save: path, --options: record<k: int>] { let data = open $data let l = $data.diversity.0 | length @@ -58,20 +74,27 @@ export def main [data: path, --save: path, --options: record<k: int>] { } | rename --column { diversity: "points" } | insert style {|it| - let color = match $it.strategy.n { - 10 => "tab:red", - 9 => "tab:orange", - 8 => "tab:olive", - 7 => "tab:blue", - 6 => "tab:purple", - 5 => "tab:green", - 4 => "tab:cyan", - 3 => "tab:brown", - 2 => "tab:pink", - _ => "tab:gray", + let color = match $it.strategy.type { + "single" => { $it.strategy.n | get-color }, + "double" => { + let c1 = $it.strategy.n | get-color | color from-string $in + let c2 = $it.strategy.m | get-color | color from-string $in + let c = $it.strategy.p + + color mix $c1 $c2 $c | color to-hex + }, + } + + let alpha = match $it.strategy.type { + "single" => 1.0, + "double" => 0.3, + } + let type = match $it.strategy.type { + "single" => "solid", + "double" => "dashed", } - { color: $color, line: { alpha: ($it.strategy.p? | default 1.0) } } + { color: $color, line: { alpha: $alpha, type: $type } } } | reject strategy | save --force /tmp/graphs.json