ggtaichi 0.2.0
New features
- Data-driven eyes (
eyes = TRUE): draw
the classic taichi dots, each centred in its own fish’s head (yin in the
top bulb, yang in the bottom bulb). yin_eye_size /
yang_eye_size and yin_eye_colour /
yang_eye_colour accept either a constant or an unquoted
data column, so a single glyph can now encode up to six
dimensions (x, y, two fills, two eyes) (#3b).
- Rotation (
angle): rotate each glyph by
a constant number of degrees or by a data column, encoding a directional
or temporal variable as orientation, and unlocking spin animations
(#3a).
- Categorical fill support:
geom_taichi() inspects the plot data at + time
and auto-selects scale_fill_manual() for discrete (factor /
character / logical) yin / yang values —
including computed expressions such as factor(week) — and
scale_fill_gradientn() for continuous ones. With the
default color vectors, discrete categories sample the ramp evenly while
skipping its palest end, so no category is invisible on a white panel.
Custom scales (objects or constructors) can be supplied via
yin_scale / yang_scale (#4a, BUG-4).
- Shared scales for directly comparable sources
(#4b):
shared_limits = TRUE gives both auto-built fill scales
common limits — the union range of the two sources (or the union of
levels when both are discrete) — so equal values read as equal ink.
Explicit limits passed through ... still win,
and mixing a discrete with a continuous source warns and ignores the
flag.
shared_legend = TRUE treats the sources as one measure:
it implies shared limits, paints both fish with yin_colors,
drops the duplicate yang guide, and titles the single legend
“yin / yang” unless yin_name is
supplied.
- The fish geoms are exported (#4d):
geom_yin_fish() and geom_yang_fish() are now
documented exports (with the GeomYinFish /
GeomYangFish ggproto objects available for extension
packages), for users who want a single fish or full manual control over
scale stacking.
remove_padding() auto mode: called
with no arguments it now detects each axis’s scale type from the plot it
is added to; the explicit "c" / "d" arguments
remain as overrides.
- New dataset
cafes_tg: a small, clearly
synthetic (seeded) espresso vs. matcha dataset whose two columns share
units — an evergreen demo for the shared-scale features and a break from
the COVID-era examples. The generating script ships in
data-raw/.
yin and yang also accept strings naming a
column (yin = "Twitter"), which previously produced a
meaningless constant fill.
geom_taichi() now returns an object with a friendly
print() method instead of dumping raw list internals at the
console.
- Animation vignette:
vignette("animations") documents how
geom_taichi() composes with gganimate
(transition_states(), spin animations via
angle, export recipes) — verified frame-by-frame against
gganimate 1.0.11. gganimate is a Suggests-only dependency.
- Vectorized rendering: each layer now draws all of
its cells as one id-batched polygon (plus one batched circle grob for
the eyes), resolved against the physical panel size at draw time via
makeContent(). Glyphs stay perfectly round under resize,
and large grids render an order of magnitude faster than the per-cell
grob building used in 0.1.0: a 1200-cell grid with eyes takes 0.24 s to
build and draw versus ~3.5 s with the per-cell approach (~15x, same
machine), with pixel-identical output.
Bug fixes
- Mapped
eye_size = 0 drew an eye (#1):
a mapped eye-size of 0 was rescaled to a positive radius,
so an eye was drawn despite the documented “0 → no eye” rule. Zeros are
now preserved and drawn without an eye.
- A zero disabled the eye-size pass-through for the whole
column: because
0 is excluded from the documented
(0, 0.5] pass-through range, a single “no eye here” zero
made every other value in an otherwise-proportional column go through
the [0.05, 0.3] rescale instead —
c(0, 0.2, 0.4) drew eyes of 0, 0.175, 0.3.
Zeros are markers rather than measurements, so they no longer take part
in that decision; the column now draws 0, 0.2, 0.4, and the
two documented rules compose as intended.
shared_legend palette mismatch (#2):
with shared_legend = TRUE and discrete fills, the yang fish
was painted with a differently-interpolated palette than the yin fish
when only one of yin_colors / yang_colors was
supplied. Both fish now use yin_colors, so identical
categories read as identical ink.
states_tg documentation (#3): the
dataset spans 31 weeks (the bundled data has 31); the documentation said
30. Corrected to 31 (the data is unchanged).
... routing (BUG-1): geom parameters
(alpha, colour, linewidth,
linetype, width, height,
na.rm, show.legend) are now real, documented
arguments of geom_taichi() and are forwarded to the
underlying fish layers. ... is reserved for options applied
to both fill scales (e.g. shared limits); per-fish scale
control goes through yin_scale /
yang_scale.
linewidth aesthetic (BUG-2): the
outline width now uses the modern linewidth aesthetic.
Passing size to geom_taichi() still works but
warns and is routed to linewidth, and an inherited
aes(size = ...) mapping is renamed through ggplot2’s
built-in deprecation path. ggtaichi now requires ggplot2 >=
3.4.0.
- Missing-argument validation (BUG-3): omitting
yin or yang errors immediately with a clear
message instead of silently producing a degenerate grey plot, and a
yin / yang column that does not exist in the
plot data errors at + time with the offending name.
- Categorical fills (BUG-4): factor / character
columns no longer trigger the cryptic “Discrete value supplied to a
continuous scale” error (see the categorical fill support above).
- Non-finite
angle and eye_size
values: the guards tested is.na(), which is
TRUE for NA and NaN but not for
Inf / -Inf. An infinite angle therefore
reached cos() / sin(), turned every vertex of
that glyph into NaN and drew nothing while warning “NaNs
produced”; an infinite mapped eye size asked grid for a circle of
infinite radius. Both now test is.finite(), so a non-finite
angle falls back to no rotation and a non-finite eye size means no eye,
exactly as NA already did.
- Non-numeric
angle columns: mapping
angle to a character column failed at draw time with the
base error “non-numeric argument to binary operator”, and mapping it to
a factor silently drew unrotated glyphs alongside
'*' not meaningful for factors warnings. Both now error at
build time with a clear message, matching how a non-numeric
eye_size column is already handled.
- Explicit discrete
limits: passing
limits through ... for a factor / character
fish aborted with “Insufficient values in manual scale” whenever the
limits held more entries than the data had levels. The auto-built
palette is now sized against the limits.
- A custom scale for the wrong aesthetic drew the wrong plot
silently: passing
e.g.
yin_scale = scale_colour_viridis_c attached the scale
to colour, which the fish never map, so the fish fell back
to ggplot2’s default blue fill gradient with no error at all.
yin_scale / yang_scale are now checked to
govern a fill aesthetic, and a value that is neither a scale object nor
a constructor function reports that instead of the base error “‘what’
must be a function or character string”.
- Non-numeric cell
width /
height failed inside setup_data()
with “non-numeric argument to binary operator”; now reported
directly.
... colliding with the scale options
geom_taichi() sets itself: guide
together with shared_legend = TRUE aborted with the base
error “formal argument guide matched by multiple actual
arguments”. The internally computed options now take precedence, so the
yang guide is still dropped while a user-supplied guide
styles the shared legend. Passing name,
values, colors, or colours
through ... now reports which per-fish argument to use
instead of raising the same base error.
theme_taichi() no longer clips text at the plot
edges: the title is now aligned with the whole plot area
(plot.title.position = "plot") and slightly smaller (15
instead of 18), so realistic titles fit at typical figure sizes, and the
right plot margin is a touch wider so an axis label sitting on the panel
boundary (common with remove_padding()) is not cut
off.
theme_taichi() element inheritance:
because the theme is composed with %+replace%, three
properties theme_bw() had set were silently dropped and
fell back to the generic text / rect parents.
The rice-paper canvas picked up a near-black 1px border around the whole
plot, the y-axis tick labels lost their right alignment and their gap
from the panel, and the legend title lost its left alignment. All three
are restored.
Documentation
- New pkgdown-only gallery article showing palettes,
data-driven eyes, rotation, categorical fills, shared scales, and
dense-grid texture.
- New “When (not) to use taichi” section in the intro
vignette: honest guidance on dense grids, luminance precision,
colorblind-safe palettes (viridis via
yin_scale /
yang_scale), and NA visibility.
- New Styling section in
?geom_taichi:
alpha, colour, linewidth and
linetype are layer-wide constants there (each has a
concrete default, so it is always forwarded as a layer parameter and
outranks an inherited mapping) — map those through
geom_yin_fish() / geom_yang_fish() instead.
width and height default to NULL
and are forwarded only when supplied, so a plot-level
aes(width = ...) does size the cells per row.
?theme_taichi now spells out its two surprising choices
— the blanked y axis title (so labs(y = ) has no effect)
and the 90-degree legend text — together with the theme()
calls that put either back.
?remove_padding now states that ...
reaches both position scales, so with axes of different types
only arguments common to continuous and discrete scales work there, and
that auto-detection reads the plot’s mapping (name the type explicitly
when x / y are mapped in a layer
instead).
?pitts_emojis now documents the actual format (HTML
<img> tags aligned row-for-row with
pitts_tg) and notes that the remote images it points at are
no longer served.
Internal
- Added a testthat suite (argument validation,
taichi_fish() geometry down to a Monte-Carlo tiling check,
parameter routing, rotation, eyes, discrete-scale selection, grob-level
rendering checks) plus vdiffr visual-regression
snapshots.
- Two gaps in that suite are closed. It now covers non-square
cells — the per-cell box following
width on x and
height on y, and the glyph radius coming from the shorter
cell side — which every coord_fixed() snapshot is blind to.
It also pins the direction of all three places rotation
is applied (taichi_fish(), the vectorised body rotation in
makeContent(), and the eye placement), so a sign error in
any one of them fails a test.
tests/testthat/setup.R holds a null device open for the
run, so the suite no longer leaves a stray Rplots.pdf in
tests/testthat/.
geom_taichi() now returns a ggtaichi_plot
object added to the plot via a ggplot_add() method, which
is what makes data-aware scale selection and shared limits
possible.
ggtaichi 0.1.0
- Initial version.
geom_taichi() turns each cell of a grid into a taichi
(yin-yang) diagram, filling the two fish with values from two data
sources.
- Added
theme_taichi() and remove_padding()
helpers.
- Bundled the
pitts_tg, states_tg, and
pitts_emojis data sets.