Skip to main content

p1_showcase/
p1_showcase.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! P1 autoformatting showcase — demonstrates all new polish features.
3//!
4//! Generates charts specifically designed to show:
5//! - Ratio-based font hierarchy & muted subtitle
6//! - Histogram bar spacing (bins nearly touching)
7//! - Area-based point sizing
8//! - Density-adaptive scatter opacity
9//! - Per-chart-type gridlines (bar = horizontal only)
10//! - Improved number formatting (SI/commas)
11//! - Area chart, pie chart, donut chart, stacked bar, grouped bar
12
13use esoc_chart::v2::{
14    area, bar, boxplot, grouped_bar, histogram, pie_labeled, scatter, stacked_bar, Chart, Layer,
15    MarkType, NewTheme,
16};
17
18fn main() -> esoc_chart::error::Result<()> {
19    // ── Simple LCG for reproducibility ────────────────────────────────
20    let mut seed: u64 = 42;
21    let mut rng = || -> f64 {
22        seed = seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
23        (seed >> 11) as f64 / (1u64 << 53) as f64
24    };
25    let mut normal = || -> f64 {
26        let u1 = rng().max(1e-15);
27        let u2 = rng();
28        (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos()
29    };
30
31    // ── 1. Dense scatter (opacity demo) ───────────────────────────────
32    let n = 500;
33    let x: Vec<f64> = (0..n).map(|_| normal() * 3.0 + 5.0).collect();
34    let y: Vec<f64> = x.iter().map(|&xi| xi * 0.8 + normal() * 2.0).collect();
35
36    let svg = scatter(&x, &y)
37        .title("Dense Scatter — Opacity & Point Sizing")
38        .x_label("feature A")
39        .y_label("feature B")
40        .size(700.0, 500.0)
41        .to_svg()?;
42    std::fs::write("dense_scatter.svg", &svg)?;
43    println!("Saved dense_scatter.svg");
44
45    // ── 2. Histogram (bins touching) ──────────────────────────────────
46    let hist_data: Vec<f64> = (0..400).map(|_| normal() * 1.5 + 10.0).collect();
47
48    let svg = histogram(&hist_data)
49        .bins(30)
50        .title("Normal Distribution — Tight Bins")
51        .x_label("value")
52        .y_label("count")
53        .size(700.0, 450.0)
54        .to_svg()?;
55    std::fs::write("hist_tight_bins.svg", &svg)?;
56    println!("Saved hist_tight_bins.svg");
57
58    // ── 3. Bar chart (horizontal-only gridlines) ──────────────────────
59    let langs = [
60        "Rust",
61        "Python",
62        "TypeScript",
63        "Go",
64        "Java",
65        "C++",
66        "Ruby",
67        "Swift",
68    ];
69    let users: Vec<f64> = vec![
70        85_000.0,
71        1_200_000.0,
72        950_000.0,
73        420_000.0,
74        780_000.0,
75        650_000.0,
76        180_000.0,
77        310_000.0,
78    ];
79
80    let svg = bar(&langs, &users)
81        .title("Language Users (thousands)")
82        .size(700.0, 450.0)
83        .to_svg()?;
84    std::fs::write("bar_large_values.svg", &svg)?;
85    println!("Saved bar_large_values.svg");
86
87    // ── 4. Area chart ─────────────────────────────────────────────────
88    let x_area: Vec<f64> = (0..60).map(|i| f64::from(i) * 0.5).collect();
89    let y_area: Vec<f64> = x_area
90        .iter()
91        .map(|&xi| (xi * 0.3).sin() * 20.0 + 25.0 + (xi * 0.1).cos() * 5.0)
92        .collect();
93
94    let svg = area(&x_area, &y_area)
95        .title("Server Load Over Time")
96        .x_label("minutes")
97        .y_label("requests / sec")
98        .size(700.0, 400.0)
99        .to_svg()?;
100    std::fs::write("area_chart.svg", &svg)?;
101    println!("Saved area_chart.svg");
102
103    // ── 5. Pie chart ──────────────────────────────────────────────────
104    let pie_vals = [35.0, 25.0, 20.0, 12.0, 8.0];
105    let pie_labels = ["Chrome", "Safari", "Firefox", "Edge", "Other"];
106
107    let svg = pie_labeled(&pie_labels, &pie_vals)
108        .title("Browser Market Share")
109        .size(500.0, 500.0)
110        .to_svg()?;
111    std::fs::write("pie_chart.svg", &svg)?;
112    println!("Saved pie_chart.svg");
113
114    // ── 6. Donut chart ────────────────────────────────────────────────
115    let svg = pie_labeled(&pie_labels, &pie_vals)
116        .donut(0.5)
117        .title("Browser Share (Donut)")
118        .size(500.0, 500.0)
119        .to_svg()?;
120    std::fs::write("donut_chart.svg", &svg)?;
121    println!("Saved donut_chart.svg");
122
123    // ── 7. Stacked bar ───────────────────────────────────────────────
124    let stack_cats = ["Q1", "Q2", "Q3", "Q4"];
125    let stack_groups = [
126        "Product A",
127        "Product A",
128        "Product A",
129        "Product A",
130        "Product B",
131        "Product B",
132        "Product B",
133        "Product B",
134        "Product C",
135        "Product C",
136        "Product C",
137        "Product C",
138    ];
139    let stack_vals = [
140        30.0, 45.0, 55.0, 40.0, // Product A
141        20.0, 25.0, 30.0, 35.0, // Product B
142        15.0, 10.0, 20.0, 25.0, // Product C
143    ];
144    // stacked_bar expects (categories, groups, values) where each row is (cat, group, value)
145    let cats_expanded: Vec<&str> = stack_cats.iter().copied().cycle().take(12).collect();
146    // Groups need to match the value ordering
147    let svg = stacked_bar(&cats_expanded, &stack_groups, &stack_vals)
148        .title("Quarterly Revenue by Product")
149        .x_label("Quarter")
150        .y_label("Revenue ($M)")
151        .size(700.0, 450.0)
152        .to_svg()?;
153    std::fs::write("stacked_bar.svg", &svg)?;
154    println!("Saved stacked_bar.svg");
155
156    // ── 8. Grouped bar ───────────────────────────────────────────────
157    let svg = grouped_bar(&cats_expanded, &stack_groups, &stack_vals)
158        .title("Quarterly Revenue — Grouped")
159        .x_label("Quarter")
160        .y_label("Revenue ($M)")
161        .size(700.0, 450.0)
162        .to_svg()?;
163    std::fs::write("grouped_bar.svg", &svg)?;
164    println!("Saved grouped_bar.svg");
165
166    // ── 9. Boxplot via v2 API ────────────────────────────────────────
167    let mut box_cats = Vec::new();
168    let mut box_vals = Vec::new();
169    for label in ["Setosa", "Versicolor", "Virginica"] {
170        let center = match label {
171            "Setosa" => 1.5,
172            "Versicolor" => 4.3,
173            _ => 5.8,
174        };
175        for _ in 0..60 {
176            box_cats.push(label);
177            box_vals.push(center + normal() * 0.5);
178        }
179    }
180
181    let svg = boxplot(&box_cats, &box_vals)
182        .title("Petal Length by Species")
183        .x_label("Species")
184        .y_label("Petal Length (cm)")
185        .size(600.0, 450.0)
186        .to_svg()?;
187    std::fs::write("boxplot_v2.svg", &svg)?;
188    println!("Saved boxplot_v2.svg");
189
190    // ── 10. Scatter with subtitle & caption (font hierarchy demo) ────
191    let x_sm: Vec<f64> = (0..30).map(f64::from).collect();
192    let y_sm: Vec<f64> = x_sm.iter().map(|&xi| xi.sqrt() * 3.0 + normal()).collect();
193
194    let chart = Chart::new()
195        .layer(Layer::new(MarkType::Point).with_x(x_sm).with_y(y_sm))
196        .title("Growth Trend Analysis")
197        .subtitle("Subtitle uses muted color and smaller font")
198        .caption("Source: synthetic data")
199        .x_label("Day")
200        .y_label("Value")
201        .size(700.0, 500.0);
202
203    let svg = chart.to_svg()?;
204    std::fs::write("font_hierarchy.svg", &svg)?;
205    println!("Saved font_hierarchy.svg");
206
207    // ── 11. Categorical scatter with many points (opacity per category) ─
208    let mut cx = Vec::new();
209    let mut cy = Vec::new();
210    let mut cc = Vec::new();
211    for (label, cx_off, cy_off) in [
212        ("Group A", 0.0, 0.0),
213        ("Group B", 5.0, 3.0),
214        ("Group C", 2.5, 6.0),
215    ] {
216        for _ in 0..150 {
217            cx.push(cx_off + normal() * 1.2);
218            cy.push(cy_off + normal() * 1.2);
219            cc.push(label);
220        }
221    }
222
223    let svg = scatter(&cx, &cy)
224        .color_by(&cc)
225        .title("Dense Categorical Scatter")
226        .x_label("x")
227        .y_label("y")
228        .size(700.0, 500.0)
229        .to_svg()?;
230    std::fs::write("dense_categorical.svg", &svg)?;
231    println!("Saved dense_categorical.svg");
232
233    // ── 12. Multi-line with grammar API (dark theme) ──────────────────
234    let epochs: Vec<f64> = (1..=40).map(f64::from).collect();
235    let loss1: Vec<f64> = epochs
236        .iter()
237        .map(|&e| 2.5 * (-e / 10.0).exp() + 0.1 + normal() * 0.02)
238        .collect();
239    let loss2: Vec<f64> = epochs
240        .iter()
241        .map(|&e| 2.0 * (-e / 15.0).exp() + 0.15 + normal() * 0.03)
242        .collect();
243
244    let chart = Chart::new()
245        .layer(
246            Layer::new(MarkType::Line)
247                .with_x(epochs.clone())
248                .with_y(loss1),
249        )
250        .layer(Layer::new(MarkType::Line).with_x(epochs).with_y(loss2))
251        .title("Model Comparison — Dark Theme")
252        .subtitle("Lower is better")
253        .x_label("Epoch")
254        .y_label("Loss")
255        .theme(NewTheme::dark())
256        .size(700.0, 450.0);
257
258    let svg = chart.to_svg()?;
259    std::fs::write("dark_theme.svg", &svg)?;
260    println!("Saved dark_theme.svg");
261
262    println!("\nAll P1 showcase charts generated!");
263    Ok(())
264}