Repository navigation
kuva v0.6.0 brings a new genome track to the library: stacked tracks sharing one genomic axis, so coverage depth, variants, amplicons and gene features line up column-for-column down the figure. Also in this release are real 2-D typeset math in labels, proper missing-data handling on the CLI, CIGAR annotation bars for BrickPlot, 15 new colour palettes, and a substantial numerical-stability sweep across the statistical plots (thanks to @eneskemalergin )
Highlights
- Genome-browser layout (#2) — the new
TrackStackprimitive and theCoveragePlotpreset, plus thekuva coveragesubcommand. Tracks are an open extension seam, andPlotTrackturns all 64 plot types into a track. - Typst math in labels (opt-in
typst-math) — stacked fractions, radicals, large operators, math italic, compiled in-process with no external binary. Plus a zero-dependencyTypstBackendthat emits CETZ markup fortypst compile. - Missing, NA and non-finite values on the CLI (#108) — recognised centrally at ingestion, so every plot behaves the same.
--na-values,--na-strategy <drop|zero|error>,--clamp-min/--clamp-max. BrickPlotCIGAR bars — per-row CIGAR annotation drawn in read space, with run-dedup, and a CIGAR that does not match its row is rejected rather than drawn.- Version stamping — every rendered file records which kuva produced it (SVG comment, PNG
Softwarechunk, PDF creator/producer), so a figure attached to a bug report identifies itself. - 15 LTC colour palettes (#112) and
Layout::with_tick_font_familyfor monospace tick labels. - Numerical-stability sweep — invalid p-values, degenerate pie charts, unstable LOESS and regression fits, runaway tick generation, and non-deterministic UpSet ordering all fixed.
Breaking changes
BrickPlotSTRIGAR input format now matches current bladerunner: bijective base-26 multi-character letters, and the|and@separators are gone. Old-format strings are rejected rather than silently rendered wrong, and reported via the newBrickPlot::strigar_issues(). Regenerate STRIGAR strings from a current bladerunner, or build rows through the builder API.kuva funnel --color-byis renamed--color-mode, resolving a collision with the data-column--color-byused everywhere else.--color-bystays as a hidden alias, so existing scripts keep working.
Upgrading
kuva = "0.6"The typst-math feature is not part of full (it pulls a ~200-crate tree and needs Rust >= 1.89). The pre-built CLI binaries on this page do ship with it, since a downloaded binary cannot opt in afterwards.
[0.6.0] — 2026-10-15
Added
-
Genome-browser layout: the
TrackStackprimitive and theCoveragePlotpreset (#2) — vertically stacked tracks that share one genomic x-axis, so coverage depth, variants, amplicons and gene features line up column-for-column down the figure. The load-bearing piece is a sharedXScale(map/invert): every track is handed the same pixel mapping, and the stack's left gutter is the maximum of all tracks' own left margins, so panels align even when their y-tick labels differ in width. This is why it is a new composition type rather than aFiguremode: eachFigurecell derives its ownmargin_leftfrom its own tick labels, so shared-x panels drift apart. Tracks are an open extension seam (Tracktrait, not an enum, so a new lane type is one trait impl and no core edits):PlotTrackwraps anyVec<Plot>, turning all 64 plot types into a track, whileIntervalTrack(labelled bands, greedy minimum-row tiling for overlapping intervals, per-row and per-item colours, labels omitted when they do not fit) andVariantTrack(typed tick groups) draw directly against theXScale. PlusTrackHeight::{Fixed,Flex}sizing,StackLayerunder/overlays withRegionHighlight, explicitEntry::Axisplacement, per-track legend sections, a title band, andXAxisFormat::{Numeric,Genomic,DateTime}whereGenomicpicks a single unit for the whole axis from its magnitude (bp/kb/Mb/Gb) so labels never mix.CoveragePlotis the first consumer and the thing #2 asks for:with_sample,with_variants,with_feature/with_features,with_region/with_regions,with_locus,with_overlaid_samples. CLI:kuva coverage. Features and genes sit above the axis, the x-axis label goes in the left gutter. The only new core plumbing isLayout::force_margin_left/right.CoveragePlotis a composite likeFigure, not aPlotenum variant, so the plot-type count stays at 64 and--emit-codedoes not apply. See Plots → Coverage Plot. -
BrickPopPlot— a population allele-frequency composite built onBrickPlot: motif bricks per haplotype row, an allele-frequency bar alongside each row (with_frequencies,with_frequency_range,with_freq_bar_color), and optional metric columns (with_metric,with_metric_column,with_metric_values) rendered as colormapped cells with an explicit missing-data colour (with_na_color, takingVec<Option<f64>>so absent is distinct from zero).sorted_by_frequency()ranks rows by frequency. LikeCoveragePlotit is a composite, not aPlotvariant. See Plots → Brick Pop Plot. -
Missing, NA and non-finite value handling on the CLI (#108) — missing values are now recognised centrally at ingestion in
DataTablerather than per subcommand, so every plot gets the same behaviour.--na-values <TOKENS>sets the token set treated as missing (comma-separated, negative sentinels allowed, e.g.--na-values -999,NA) on top of the built-in empty/NA/NaN/null/N/Aset, and--na-strategy <drop|zero|error>chooses what happens to a row with a missing value in a selected numeric column: drop the row (default), substitute 0, or fail outright (the pre-#108 behaviour).--clamp-min <N>/--clamp-max <N>clip values to a bound, which also gives-inf/infsomewhere finite to land, e.g. capping an infinite-log10(p)with--clamp-max 300. Extended beyond the numeric plots to bar and parallel-coordinates, where a missing value in any axis previously had no defined meaning. -
Version stamping in output — rendered files now record which kuva produced them, for triaging an issue from an attached figure. All three file backends record it in their format's own metadata slot: SVG writes a
<!-- Generated by kuva X.Y.Z -->comment, PNG writes a standardSoftwaretEXtchunk readingkuva X.Y.Z, and PDF sets the document'screator/producermetadata. The version is exposed to library users askuva::VERSION. -
BrickPlotCIGAR bars —with_cigars([...])draws a thin band above each row, coloured by that read's CIGAR operations, so a reader can see which bases are reference-supported and which are insertions or soft clips. Drawn in read space, the same coordinate as the bricks, so an operation always lines up with the bases beneath it:M/=/X/I/Sconsume read bases and get width, whileD/Nconsume only reference and render as a zero-width caret rather than opening a gap in the row. Consecutive rows sharing a CIGAR collapse to a single bar (with_cigar_dedup, default on) bracketed with anxNcount in the gutter (with_cigar_run_bracket), so a block of identical reads states its CIGAR once and a bar appears only where something changes; vertical space is charged only to rows that draw a bar, so an unchanging run keeps the normal pitch.with_sort(BrickSort::Cigar)groups identical CIGARs so dedup draws the fewest possible bars, andBrickSort::Custom(Vec<usize>)accepts an externally computed ranking (e.g. a caller's distance-from-consensus order) without kuva needing to know how it was derived. Op colours are reserved outside the motif palette so a motif brick can never be mistaken for an insertion, and are overridable viawith_cigar_colors. The CIGAR covers every base the row draws, flanks included (left flank + STRIGAR + right flank for awith_flanked_strigarsrow), and the bar starts at the row's first drawn base, so flanking sequence is annotated too;row_base_len(i)returns the length a row's CIGAR must consume. A CIGAR whose read-consuming length disagrees with its row is rejected rather than drawn and reported bycigar_issues(): a bar that is short by a few bases misaligns every operation after the first while still looking like a valid annotation. Supply the CIGAR from whatever produced the row rather than re-deriving it from locus coordinates, since only that producer knows where its own span begins and ends.with_row_heightandFigurerow sizing both account for the space the bars take, so brick rows keep their requested pixel height.cigar_bar_count()reports how many bars will be drawn. -
Layout::with_tick_font_family: set the font family of axis tick labels separately from titles, axis labels, and legends, e.g..with_tick_font_family("monospace")for sequence-context category labels. Tick labels are wrapped in a<g font-family>group, so output without the option is unchanged. Coverage spans every place kuva draws a tick label, not just the cartesian axes: x/y/x2/y2 ticks and Manhattan chromosome labels, plus the pixel-space and composite plots that draw their own (polar ring and theta labels, ternary edge percentages, radar ring values and spoke names, parallel-coordinates per-axis values, 3D x/y/z ticks, and colorbar ticks). Titles stay in the main family throughout, including the ones that sit where a tick label looks like it should be: ternary corner labels, parallel axis names, 3D axis labels, and the rotated colorbar label. Width reservation follows the face:text_metricsgains aFontStyle::Monotable generated from the bundled DejaVu Sans Mono (cargo run --example gen_text_metrics, +509 RLE runs, with the same drift guards as the sans faces), and a tick label in a monospace family is measured against it. A fixed-width face is narrower than DejaVu Sans for digits (0.602 em vs 0.636 em) but nearly twice as wide for thin glyphs (.is 0.602 em vs 0.318 em), so sans-measured margins under-reserved a decimal axis and clipped it;with_font_family("monospace")now reserves tick labels the same way. The family decides, by word ("DejaVu Sans Mono","ui-monospace") or by a list of common fixed-width faces that carry nomonoin their name ("Consolas","Fira Code", …);"Monotype Corsiva"and other proportional faces keep the sans metrics. The genome-browser composite carries the option itself, since it builds aLayoutper track:TrackStack::with_tick_font_family/CoveragePlot::with_tick_font_familyset the shared genomic axis and every track's y-axis ticks at once (newTrack::set_tick_font_familytrait method, defaulted to a no-op so annotation lanes are unaffected). The PDF backend now resolves the genericmonospacefamily to the bundled DejaVu Sans Mono instead of a system font such as Courier New; PNG and terminal output ignore the option exactly as they already ignorewith_font_family. See Reference → Layout & Axes. -
Typst tier for math in labels (new opt-in
typst-mathfeature) — labels containing$...$may now be typeset with the Typst compiler (linked as a library — no external binary) for real 2-D math: stacked fractions, radicals with vinculum, large operators, math italic. The whole label is compiled and embedded as an SVG fragment (SVG backend), pixmap (PNG), or through the PDF pipeline, with math color following the label color.typst-mathis not part offull: it pulls a ~200-crate dependency tree and needs Rust >= 1.89, sofullstays the core backend set (embed_font+png+pdf) and the typesetter is enabled explicitly withcargo build --features typst-math. The pre-built CLI binaries on the releases page do ship with it, since a downloaded binary cannot opt in afterwards. Builds on the always-on inline-Unicode lookup tier from 0.3.0, which remains the fallback everywhere: the terminal backend, builds withouttypst-math, and any label Typst fails to compile (one warning per distinct label). Bundles New Computer Modern Math (~0.75 MB compressed) as the math font. Layout reserves the fragment's measured extent rather than one nominal text line, via a newComputedLayout::x_label_math_extra(andComputedLayoutis now#[non_exhaustive], so future layout fields are not breaking additions), so a deep descender (the denominator of$\frac{-b \pm \sqrt{b^2-4ac}}{2a}$) clears the canvas bottom with the same gap ordinary text gets. Note: Typst math is not LaTeX (mcis one identifier — writem c). See Reference → Math in Labels. -
Math in
TextPlotbodies (typst tier) —$...$inside a markdown body is spliced into the wrapped paragraph as a typeset fragment: kuva's own word-wrap treats each fragment as one unbreakable word (sized by its typeset width), surrounding words stay real selectable text in SVG/PDF, punctuation directly after math stays flush ($x^2$.), and a line holding a tall fragment (stacked fraction, radical) grows its leading so neighbouring lines never collide. Terminal output and builds withouttypst-mathlower body math to inline Unicode exactly as before. -
TypstBackend(opt-intypstfeature, zero deps; not infull) — emit a CETZ-based Typst document for external compilation withtypst compile;$...$regions become native Typst math, typeset with fonts matching the surrounding document. Full primitive coverage:Path(arrowheads, chord ribbons, sankey flows, venn outlines) lowers to CETZmerge-pathcalls with arcs converted to cubic Béziers, and clip regions map to Typst'sbox(clip: true)— so quiver/network arrowheads and plot-area clipping survive the round trip. Library-only: the CLI's-ostill accepts.svg/.png/.pdfonly, so.typis emitted by constructing aTypstBackendin Rust. -
CoveragePlotcoverage-threshold lines —with_coverage_threshold(depth)/with_coverage_threshold_labeled(depth, label)draw a dashed horizontal line at a given depth on every coverage track (e.g. a minimum-coverage cutoff). CLI:kuva coverage --min-coverage <depth>(repeatable). Built on newPlotTrack::with_hline/with_reference_linesupport in theTrackStackprimitive, so anyPlotTrackcan carry horizontal reference lines in its own y-scale. -
BrickPlotmarker lines —with_vline(x)/with_vline_labeled(x, label)draw a dashed vertical line across all rows at a reference coordinate (locus, primer boundary, variant position), andwith_marker_line(ReferenceLine)takes a fully-styled line. Drawn in the brick's own reference-coordinate system, so they respectwith_x_offset/with_x_origin. (ReferenceLine/Orientationnow deriveDebug.) -
Survival clinical figures.
SurvivalPlotgains three survminer-style options for publication-quality Kaplan-Meier figures:.with_risk_table(true)draws a number-at-risk table below the plot (one row per group, counts at each x-axis tick; space is reserved automatically via a newLayout::risk_table_rows/ComputedLayout::risk_table_extrabottom-margin mechanism);.with_median_lines(true)draws dashed median-survival reference lines; and.with_logrank_pvalue(true)computes the log-rank test p-value from the raw data (exact Mantel-Haenszel for two groups,Σ (O−E)²/Eapproximation for 3+) via a new chi-square survival function, annotating it and overriding anywith_pvalue_text. CLI:kuva survival --risk-table --median-lines --logrank. -
Force-directed (ggrepel-style) label placement. A new
LabelStyle::Repelvariant runs a sharedrender_utils::repel_labelsforce-directed pass (labels pushed off each other and off the points in 2-D, with thin leader lines back to each). Wired intoVolcanoPlotandManhattanPlot(CLI--label-style repel), andScatterPlotgains per-point labels for the first time:.with_labels(iter)+.with_label_style(…)/.with_repel_labels(), with CLIkuva scatter --label-col <COL> --label-style <nudge|exact|repel>. The Exact/Nudge/Arrow/Repel rendering is now one shareddraw_labelspath across all three plot types (deduplicating the previous per-plot copies). -
Scatter marker shapes and LOESS smoothing.
MarkerShapegainsTriangleDown,Star,Pentagon, andHexagon(now 10 shapes), lifting every plot that reuses the marker set; aMarkerShape::parsehelper and the CLIkuva scatter --marker <shape>flag expose them. A newTrendLine::Loess { span }variant (plusScatterPlot::with_loess()/with_loess_span(f)and the sharedrender_utils::loesslocally-weighted-regression helper) overlays a LOESS/LOWESS smoother for non-linear trends; CLI--loess/--loess-span <F>. LOESS takes precedence over--trendwhen both are given. -
StripPlot::with_horizontal(bool)— horizontal orientation (categories on the Y axis, values on X; seabornorient='h', ggplot2coord_flip), completing the horizontal-orientation set alongsideBoxPlot/ViolinPlot. Jitter and swarm spread run vertically within each row. The renderer already supported the swap for all three layout styles; this wires the field,bounds(), category-axis labels, and the CLIkuva strip --horizontal(both--yand--group-colmodes) through to it. -
kuva strip --opacity <0..1>— CLI access to marker fill opacity (the existingStripPlot::with_marker_opacity). Semi-transparent markers reveal density where dense points overlap, which matters for real-data strip/beeswarm plots at large N. -
Histogram distribution modes — five matplotlib/seaborn-parity options on
Histogram, all sharing one binning path (sobounds()and the renderer always agree):.with_step(bool)draws outline-only staircases for clean overlays (matplotlibhisttype='step');.with_cumulative(bool)accumulates counts left-to-right (cumulative=True);.with_bin_method(BinMethod::{Sturges,Scott,FreedmanDiaconis})picks the bin count from the data, overriding.with_bins;.with_weights(Vec<f64>)weights each sample into its bin (weights=); and.with_group(data, color, label)+.with_stacked(bool)stack multiple series per bin (stacked=True, seabornmultiple='stack'). CLI:--step,--cumulative,--bin-method <sturges|scott|fd>,--weight-col <COL>, and--stacked(stacks the--ycolumns instead of overlaying them).BinMethod/HistGroupare re-exported fromkuva::plot. -
LTC colour palettes (#112) — 15 curated qualitative palettes from loukesio/ltc-color-palettes (MIT), added as
Paletteconstructors (Palette::paloma()…Palette::casa_natal()). CLI--paletteaccepts each by name with an optionalltc-prefix (e.g.ltc-maya). The Reference → Color Palettes page gains a visual swatch section with copyable hex values.
Changed
BrickPlotSTRIGAR input format updated to match bladerunner — the motif-run encoding now uses bijective base-26 multi-character letters (so a row may reference more than 26 distinct motifs), and the|and@separators were dropped. This is a breaking input change, and it now fails loudly. The tokeniser skips characters it cannot use, so an old-format string would otherwise lose its separators and still render: a row with the wrong bricks in the wrong order, indistinguishable from a good one.with_strigarsnow validates each row first and rejects any carrying|or@, reporting it asStrigarIssue::LegacyFormatvia the newBrickPlot::strigar_issues()(mirroringcigar_issues()); characters that are neither a count nor a base-26 letter are reported asStrigarIssue::Unparseable. A rejected row keeps its slot but draws no bricks, and validation runs before the motif-frequency passes so a bad row cannot steer the global canonical and colour assignment shared by every other row. Regenerate STRIGAR strings from a current bladerunner, or construct rows through the builder API instead. Motif colour assignment was also made deterministic (it previously depended on hash iteration order, so the same input could come back with different colours between runs) and the inter-brick gap calculation was fixed.kuva funnel: the color-mode flag is renamed--color-by→--color-modeto remove a naming collision. Everywhere else--color-byselects a data column (ColSpec), but onfunnelit chose a bar color mode (uniform/stage/gradient). It is now--color-mode, with--color-bykept as a hidden backward-compatible alias so existing scripts keep working.
Fixed
-
Mosaic cell labels showing both a percentage and a value rendered as one mangled line.
MosaicPlotbuilt the label as a single"46.7%\n70"string, but a\ninside one text primitive is not a line break for any backend: SVG collapses it to a space under XML whitespace handling, and the terminal backend rewrites it to U+FFFD along with every other control character (the GHSA-3c48 escape-injection guard), sokuva mosaic --show-values --terminalprinted46.7%<?>70. It also defeated the fit test, which measures the widest line rather than the joined string, so a label that passed could still overflow its cell. The two lines are now separate primitives, stacked and centred as a block.--show-valuesturns this on by default, sinceshow_percentsis already true. Single-line labels are positioned exactly as before. -
LollipopPlotpoint labels were clipped when the labelled point was the tallest one. A per-point label is drawn clear of its dot (dot_radius + 4 px, then the glyphs) entirely in pixel space, butbounds()reported only the dot's data value, so the axis maximum landed on the dot itself and the plot-area clip rectangle sliced the top off the text. It bit hardest when the data maximum fell exactly on a tick boundary:auto_nice_range_cappedthen caps the axis atraw_max + 5 % of the span, which is 11 px of headroom against a label needing about 17 px. (Same shape as the Pareto cumulative-axis clipping fixed in v0.5.0.)bounds()now reserves headroom above the tallest labelled point, and below the lowest labelled point for the below-baseline labels, so the whole label always falls inside the plot area. The canonical case is a mutation landscape whose hotspot is also the most-mutated residue:docs/src/assets/lollipop/mutation_landscape.svghadR248Wcut in half. -
BW mode drew the lightest continuous cells pure white, i.e. invisible. BW mode maps continuous colormaps onto ColorBrewer Greys, whose ramp starts at
#ffffff— the canvas colour. For a dense grid (heatmap, clustermap) that was merely faint, but for plots that omit empty cells (HexbinPlot,CalendarPlot) a minimum-value cell was indistinguishable from no cell at all: in a 3,000-point hexbin, 96 of 204 hexagons rendered at or above#f0f0f0and 30 were pure white. The BW ramp is now floored at#efefefand still reaches full black, so the lightest cells stay visible with essentially the whole tonal range intact. This matches the floor the categoricalBW_GREYSsequence already applied for the same reason. Fills and colorbars share onebw_colormap()helper, so a bar always describes the ramp its cells were drawn with. Affects every BW continuous plot: hexbin, heatmap, calendar, contour, clustermap, 2D histogram, dot plot, quiver, 3D surface, and continuous dice plots. -
BrickPlotCIGAR legend labels were drawn outside the legend box. Legend entries are collected incollect_legend_entrieswhile the legend width is reserved separately inLayout::auto_from_plots, and only the former knew about the CIGAR operation swatches. The box was sized to the widest motif label ("CAG", 25.89 px) while"insertion"needed roughly 54 px, so the op labels ran past the box border and off the canvas edge. Both sites now share acigar_legend_ops()helper, so the entry list and the width reservation cannot drift apart again. -
All-numeric column names could not be selected on the CLI (#109) —
--x 2024parses toColSpec::Index(2024), so a header column literally named2024(a year, etc.) was unreachable and failed with an out-of-range error.DataTable::resolvenow falls back to a header column named by that number when the index is out of range; in-range indices are untouched, so--x 0still always means the first column (fully backward compatible). Applied to the TSV/CSV and parquet-projection paths, and the auto axis/series labels (scatter/line/twin-y/parallel/radar) route through the shared resolver so the label shows the real column name.- Added explicit disambiguation prefixes on any column argument, parsed centrally in
ColSpec::from_strso they work across every subcommand:name:X/col:Xforce a column name (even an all-digit one, e.g. select a column literally named1via--x name:1), andidx:N/index:N/#Nforce a pure 0-based index that never resolves to a name (an out-of-rangeidx:errors instead of falling back). A bare token keeps the index-first-with-fallback heuristic. - Added column ranges and globs on multi-column arguments (
--y,--value-cols,--samples,--axes): index rangesa..b/a..=b/a../..b/.., and header-name globssum_*/*_A/*mid*(*matches any run). They expand against the parsed table (deduped, order-preserving) viaDataTable::expand_columns; a lone range/glob still routes into the plot's multi-column path. Wired through scatter/line/bar/histogram/box/violin/strip/density/ridgeline/parallel/radar/coverage.
- Added explicit disambiguation prefixes on any column argument, parsed centrally in
-
Histogram legends never rendered —
Layout::auto_from_plotshad nohas_legendwiring forPlot::Histogram, so.with_legend(...)(and the CLI--legend) silently produced no legend. Now wired for both the primary series and stacked/overlaid groups, matchingcollect_legend_entries. -
Single-slice pie chart rendered as a line (#117) — a
PiePlotwhose slices sum to one full turn drew nothing but a line. An SVG arc is defined by its endpoints, so a 360° sweep starts and ends at the same point and degenerates to zero length; the slice is now drawn as two 180° arcs. Donuts (with_inner_radius) are covered too: the ring is emitted as a single subpath (outer arcs,Linwards, inner arcs reversed) matching the existing partial-sweep donut, rather than as two subpaths whose hole would depend on the nonzero fill rule being applied across subpaths — which the SVG spec provides but kuva's raster and terminal backends do not. -
Axis ticks: linear major and minor ticks retain small values and stop repeating positions at large offsets. Automatic labels no longer display tiny nonzero values as zero, and histogram tick selection searches requested intervals instead of scanning every bin multiplier. A tick step far below the axis's own resolution (for example an explicit
with_x_tick_stepnear one ULP) is now rejected outright instead of generating positions until the process runs out of memory. -
ROC thresholds and DeLong AUC now use exact score equality for ties. Close but distinct scores remain ordered, preventing a pair from counting as both a win and a tie and keeping the reported AUC consistent with the curve.
-
Precision-recall thresholds now also use exact score equality for ties, so close but distinct scores stay ordered and infinite scores no longer stop the curve from finishing.
-
ROC and precision-recall groups with a NaN score now produce no curve instead of running out of memory. The legend no longer shows an AUC of 0.000 for a group without a curve.
-
Scatter LOESS fits: scaled local coordinates and weighted squared deviations avoid distorted curves with large coordinate offsets, sparse neighborhoods, or large y values. Small x units no longer trigger a fixed-cutoff constant fit. Non-finite observations produce no smoother, and unrepresentable predictions are omitted.
-
Linear regression and Pearson correlation keep their results across large coordinate offsets and small or large units. Scatter trend lines and bounds use centered predictions, and undefined or unrepresentable statistics no longer return non-finite coefficients.
-
2D histogram correlation annotations are omitted when Pearson correlation is undefined, instead of panicking on too few points or constant coordinates.
-
Scatter trend lines on constant y now draw the flat least-squares line and omit only the
rannotation, which is undefined there rather than zero. Previously no line was drawn at all. -
Invalid volcano p-values are no longer drawn as the most significant points. A NaN or negative p-value used to be placed at the top of the plot and could receive a top label, and a p-value above 1 was placed below the axis; such points are now left out, and
kuva volcanorejects them with the row number. Data whose p-values are all exactly zero also keeps a finite p-value floor, instead of placing every point at-inf. -
Invalid Manhattan p-values are no longer drawn as the most significant points. A NaN or negative p-value used to be placed at the top of the plot and could receive a top label, and a p-value above 1 was placed below the axis; such points are now left out, and
kuva manhattanrejects them with the row number. Data whose p-values are all exactly zero also keeps a finite p-value floor, instead of placing every point at-inf. -
Pie charts with negative values or a zero total no longer write NaN coordinates or draw overlapping arcs. No slices are drawn, the legend omits percentages, and
kuva piereports the negative value, the zero total, or input with no values to draw at all (including--count-byover a table with no rows). -
Manhattan plots with no plottable point no longer write an unmappable chromosome label position into the output; with no x range to map onto, the labels are omitted along with the points.
-
UpSet plots silently merged sets past the 64th. An intersection is a
u64bitmask, but nothing enforced that: a 65th set shifted past the top bit, which panicked in a debug build and in release wrapped around to alias set 0, giving a wrong plot with no warning.kuva upsetnow rejects more than 64 columns, the builders drop sets past the limit instead of aliasing them, andUpSetPlot::MAX_SETSnames it. -
kuva upsetoutput varied between runs. Intersections with equal counts, and all intersections with--sort natural, appeared in a different order each time. They now follow the order of their set membership, asUpSetPlot::with_setsalready did.
Pull Requests/Contributions
- Add math in labels (lookup tier + opt-in Typst tier) by @gabo515 in #88
- Uses two arcs for one-slice pie by @samcwiley in #118
- Keep ROC and precision-recall ties consistent by @eneskemalergin in #119
- Keep scatter LOESS fits stable across coordinate scales by @eneskemalergin in #120
- Keep axis ticks finite and distinct by @eneskemalergin in #121
- Stabilize linear regression and Pearson correlation by @eneskemalergin in #122
- Fix invalid p-values, pie values and UpSet order by @eneskemalergin in #123
- Add Layout::with_tick_font_family to set the tick label font separately by @clintval in #124
New Contributors
- @samcwiley made their first contribution in #118
- @clintval made their first contribution in #124
Full Changelog: v0.5.0...v0.6.0
Edit: I re-ran the release actions that build the binaries with some changes. There are now -typst binaries that include the full typst backend, and then those without. Those without will have a much smaller binary size.