Skip to content

Commit 01d5439

Browse files
authored
Resuppress dummy axes on polar plots (#430)
* Consolate `is_free` with dummy scale for axis suppression mechanism * fix tick justification bug in angular axis
1 parent 841ac67 commit 01d5439

1 file changed

Lines changed: 37 additions & 9 deletions

File tree

src/writer/vegalite/projection.rs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,24 +186,29 @@ struct AxisInfo {
186186
domain: Option<(f64, f64)>,
187187
breaks: Vec<f64>,
188188
labels: Vec<(f64, String)>,
189-
is_free: bool,
189+
suppress: bool,
190190
}
191191

192192
impl AxisInfo {
193193
fn new(aesthetic: &str, scales: &[Scale], facet: Option<&crate::plot::Facet>) -> Self {
194-
let (domain, labels) = match scales.iter().find(|s| s.aesthetic == aesthetic) {
194+
let scale = scales.iter().find(|s| s.aesthetic == aesthetic);
195+
let (domain, labels) = match scale {
195196
Some(s) => (s.numeric_domain(), s.break_labels()),
196197
None => (None, Vec::new()),
197198
};
198199
// Set domain to None if zero-range
199200
let domain = domain.filter(|(min, max)| (max - min).abs() > f64::EPSILON);
200201
let breaks = labels.iter().map(|(v, _)| *v).collect();
201-
let is_free = facet.is_some_and(|f| f.is_free(aesthetic));
202+
// Free facet scales have per-panel domains that don't match the global
203+
// positions used for decoration; dummy scales are stat-injected placeholders
204+
// with no meaningful domain to label.
205+
let suppress =
206+
facet.is_some_and(|f| f.is_free(aesthetic)) || scale.is_some_and(|s| s.is_dummy());
202207
Self {
203208
domain,
204209
breaks,
205210
labels,
206-
is_free,
211+
suppress,
207212
}
208213
}
209214
}
@@ -403,7 +408,7 @@ impl ProjectionRenderer for PolarProjection {
403408
impl PolarProjection {
404409
fn grid_rings(&self, theme: &Value) -> Vec<Value> {
405410
let p = &self.panel;
406-
if p.radial.is_free {
411+
if p.radial.suppress {
407412
return Vec::new();
408413
}
409414
let Some((domain_min, domain_max)) = p.radial.domain else {
@@ -464,7 +469,7 @@ impl PolarProjection {
464469

465470
fn grid_spokes(&self, theme: &Value) -> Vec<Value> {
466471
let p = &self.panel;
467-
if p.angle.is_free || p.angle.domain.is_none() {
472+
if p.angle.suppress || p.angle.domain.is_none() {
468473
return Vec::new();
469474
}
470475
if p.angle.breaks.is_empty() {
@@ -509,7 +514,7 @@ impl PolarProjection {
509514

510515
fn radial_axis(&self, theme: &Value) -> Vec<Value> {
511516
let p = &self.panel;
512-
if p.radial.is_free {
517+
if p.radial.suppress {
513518
return Vec::new();
514519
}
515520
if p.radial.domain.is_none() {
@@ -661,7 +666,7 @@ impl PolarProjection {
661666

662667
fn angular_axis(&self, theme: &Value) -> Vec<Value> {
663668
let p = &self.panel;
664-
if p.angle.is_free {
669+
if p.angle.suppress {
665670
return Vec::new();
666671
}
667672
let Some((domain_min, domain_max)) = p.angle.domain else {
@@ -722,7 +727,7 @@ impl PolarProjection {
722727
.collect();
723728
let theta = p.expr_normalize_theta("datum.v");
724729

725-
let tick_just: f64 = if p.is_full_circle { 0.5 } else { 0.0 };
730+
let tick_just: f64 = 0.0;
726731

727732
let outer_cx = p.expr_x(&outer_s, &theta);
728733
let outer_cy = p.expr_y(&outer_s, &theta);
@@ -2131,6 +2136,29 @@ mod tests {
21312136
assert!(!proj.angular_axis(&theme).is_empty());
21322137
}
21332138

2139+
#[test]
2140+
fn dummy_scale_suppresses_decoration() {
2141+
use crate::naming::stat_column;
2142+
use crate::plot::types::ArrayElement;
2143+
2144+
let dummy_sentinel = stat_column("dummy");
2145+
let mut dummy = Scale::new("pos1");
2146+
dummy.input_range = Some(vec![ArrayElement::String(dummy_sentinel)]);
2147+
2148+
let angle = scale_with_breaks("pos2", (0.0, 360.0), vec![90.0, 180.0, 270.0]);
2149+
let scales = vec![dummy, angle];
2150+
2151+
let proj = PolarProjection {
2152+
panel: PolarContext::new(None, None, &scales),
2153+
};
2154+
let theme = Value::Null;
2155+
2156+
assert!(proj.grid_rings(&theme).is_empty());
2157+
assert!(proj.radial_axis(&theme).is_empty());
2158+
assert!(!proj.grid_spokes(&theme).is_empty());
2159+
assert!(!proj.angular_axis(&theme).is_empty());
2160+
}
2161+
21342162
// =========================================================================
21352163
// Discrete channel: indexof expression
21362164
// =========================================================================

0 commit comments

Comments
 (0)