mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-03 17:16:14 -06:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb840345df | ||
|
|
3bc364e712 |
@@ -203,11 +203,12 @@ pub(super) fn pie(ctx: &mut Ctx, chart: &Chart, data: &ChartData, area: Bx, inne
|
||||
|
||||
// ---------------------------------------------------------------- treemap
|
||||
|
||||
/// Worst aspect ratio of a row with the given sum, smallest and largest area.
|
||||
fn worst(s: f64, mn: f64, mx: f64, side: f64) -> f64 {
|
||||
fn worst(row: &[f64], side: f64) -> f64 {
|
||||
let s: f64 = row.iter().sum();
|
||||
if s <= 0.0 || side <= 0.0 {
|
||||
return f64::INFINITY;
|
||||
}
|
||||
let (mn, mx) = row.iter().fold((f64::INFINITY, 0.0f64), |(a, b), v| (a.min(*v), b.max(*v)));
|
||||
let s2 = s * s;
|
||||
let w2 = side * side;
|
||||
(w2 * mx / s2).max(s2 / (w2 * mn))
|
||||
@@ -224,8 +225,6 @@ fn squarify(items: &[(usize, f64)], r: Bx) -> Vec<(usize, Bx)> {
|
||||
let mut out = Vec::with_capacity(items.len());
|
||||
let mut rem = r;
|
||||
let mut row: Vec<(usize, f64)> = Vec::new();
|
||||
// The row's sum, smallest and largest area, kept as items join so each step is O(1).
|
||||
let (mut sum, mut mn, mut mx) = (0.0f64, f64::INFINITY, 0.0f64);
|
||||
let mut i = 0;
|
||||
let layout = |row: &[(usize, f64)], rem: &mut Bx, out: &mut Vec<(usize, Bx)>| {
|
||||
let s: f64 = row.iter().map(|x| x.1).sum();
|
||||
@@ -257,14 +256,15 @@ fn squarify(items: &[(usize, f64)], r: Bx) -> Vec<(usize, Bx)> {
|
||||
while let Some(&(j, v)) = items.get(i) {
|
||||
let a = v * scale;
|
||||
let side = rem.w.min(rem.h) as f64;
|
||||
if row.is_empty() || worst(sum + a, mn.min(a), mx.max(a), side) <= worst(sum, mn, mx, side) {
|
||||
let cur: Vec<f64> = row.iter().map(|x| x.1).collect();
|
||||
let mut with = cur.clone();
|
||||
with.push(a);
|
||||
if row.is_empty() || worst(&with, side) <= worst(&cur, side) {
|
||||
row.push((j, a));
|
||||
(sum, mn, mx) = (sum + a, mn.min(a), mx.max(a));
|
||||
i += 1;
|
||||
} else {
|
||||
layout(&row, &mut rem, &mut out);
|
||||
row.clear();
|
||||
(sum, mn, mx) = (0.0, f64::INFINITY, 0.0);
|
||||
}
|
||||
}
|
||||
if !row.is_empty() {
|
||||
@@ -279,7 +279,6 @@ pub(super) fn treemap(ctx: &mut Ctx, chart: &Chart, data: &ChartData, area: Bx)
|
||||
s.values.iter().take(MAX_MARKERS).enumerate().filter_map(|(j, v)| v.filter(|v| *v > 0.0).map(|v| (j, v))).collect();
|
||||
items.sort_by(|a, b| b.1.total_cmp(&a.1));
|
||||
let f = Fmt::new(&s.number_format);
|
||||
let value_of: std::collections::HashMap<usize, f64> = items.iter().copied().collect();
|
||||
for (j, b) in squarify(&items, area) {
|
||||
let fill = point_color(data, j);
|
||||
ctx.rect(b.x, b.y, b.w, b.h, fill, Some((WHITE, 1.5)));
|
||||
@@ -287,7 +286,7 @@ pub(super) fn treemap(ctx: &mut Ctx, chart: &Chart, data: &ChartData, area: Bx)
|
||||
let name = ctx.fit(&cat_label(data, j), LABEL_SIZE, b.w - 8.0);
|
||||
ctx.text(b.x + 4.0, b.y + 4.0, &name, LABEL_SIZE, on(fill), (HAlign::Left, VAlign::Top));
|
||||
if chart.data_labels && b.h > LABEL_SIZE * 3.2 {
|
||||
let v = value_of.get(&j).copied().unwrap_or(0.0);
|
||||
let v = items.iter().find(|x| x.0 == j).map(|x| x.1).unwrap_or(0.0);
|
||||
let t = ctx.fit(&f.format(v, None), LABEL_SIZE, b.w - 8.0);
|
||||
ctx.text(b.x + 4.0, b.y + 4.0 + LABEL_SIZE * 1.4, &t, LABEL_SIZE, on(fill), (HAlign::Left, VAlign::Top));
|
||||
}
|
||||
|
||||
@@ -136,9 +136,6 @@ pub(crate) fn fixed(a: f64, decimals: usize) -> (String, String) {
|
||||
/// General format squeezed into `max_chars` characters. Excel shows fewer decimals, then
|
||||
/// scientific notation, then "####" when a column is narrow; `None` means the caller paints "####".
|
||||
pub fn format_general_fit(n: f64, max_chars: usize) -> Option<String> {
|
||||
// The widest fixed rendering of a double is about 330 characters; a larger request only
|
||||
// pads zero decimals the trim below throws away.
|
||||
let max_chars = max_chars.min(1024);
|
||||
if !n.is_finite() {
|
||||
return None;
|
||||
}
|
||||
@@ -217,8 +214,6 @@ mod tests {
|
||||
fn general_fit() {
|
||||
assert_eq!(format_general_fit(1e11, 11).as_deref(), Some("1E+11"));
|
||||
assert_eq!(format_general_fit(12345678901.0, 11).as_deref(), Some("12345678901"));
|
||||
// A huge width neither allocates width-sized padding nor changes the text.
|
||||
assert_eq!(format_general_fit(1.5, usize::MAX).as_deref(), Some("1.5"));
|
||||
assert_eq!(format_general_fit(123456789012.0, 11).as_deref(), Some("1.23457E+11"));
|
||||
assert_eq!(format_general_fit(0.1 + 0.2, 11).as_deref(), Some("0.3"));
|
||||
assert_eq!(format_general_fit(1.0 / 3.0, 11).as_deref(), Some("0.333333333"));
|
||||
|
||||
@@ -375,7 +375,7 @@ pub fn formula_refs(text: &str) -> Vec<(usize, usize, RangeRef, Option<String>,
|
||||
let offset = text.len() - body.len();
|
||||
let Ok(toks) = gridcraft_engine::formula::lexer::tokenize(body) else { return vec![] };
|
||||
let mut out: Vec<(usize, usize, RangeRef, Option<String>, usize)> = Vec::new();
|
||||
let mut seen: Vec<String> = Vec::new();
|
||||
let mut seen: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
|
||||
let mut i = 0;
|
||||
while i < toks.len() {
|
||||
use gridcraft_engine::formula::lexer::Tok;
|
||||
@@ -407,13 +407,8 @@ pub fn formula_refs(text: &str) -> Vec<(usize, usize, RangeRef, Option<String>,
|
||||
continue;
|
||||
};
|
||||
let key = body.get(start..end).unwrap_or("").replace('$', "").to_ascii_uppercase();
|
||||
let idx = match seen.iter().position(|k| *k == key) {
|
||||
Some(k) => k,
|
||||
None => {
|
||||
seen.push(key);
|
||||
seen.len() - 1
|
||||
}
|
||||
};
|
||||
let next = seen.len();
|
||||
let idx = *seen.entry(key).or_insert(next);
|
||||
out.push((start + offset, end + offset, range, sheet, idx % REF_COLOR_COUNT));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -278,6 +278,9 @@ fn paint_cell_decorations(q: &QuadrantPaint<'_>, c: CellRef, rect: Rect, lk: &Lo
|
||||
false
|
||||
}
|
||||
|
||||
/// Most repeats of a fill character laid out in one cell.
|
||||
const MAX_FILL_REPEATS: usize = 8192;
|
||||
|
||||
fn paint_data_bar(p: &egui::Painter, rect: Rect, frac: f32, rgb: [u8; 3], gradient: bool) {
|
||||
let bar = Rect::from_min_size(pos2(rect.left() + 2.0, rect.top() + 2.0), vec2((rect.width() - 4.0) * frac, rect.height() - 4.0));
|
||||
let col = theme::color32(rgb);
|
||||
@@ -432,7 +435,8 @@ fn paint_cell(q: &QuadrantPaint<'_>, painted: &(CellRef, Rect, Look, Option<CfLo
|
||||
{
|
||||
let base_w = w;
|
||||
let cw = p.layout_no_wrap(ch.to_string(), font.clone(), color).size().x.max(1.0);
|
||||
let n = (((avail_w - 2.0 * pad) - base_w) / cw).floor().max(0.0) as usize;
|
||||
// A merged cell's width counts offscreen columns: no screen shows more repeats than this.
|
||||
let n = ((((avail_w - 2.0 * pad) - base_w) / cw).floor().max(0.0) as usize).min(MAX_FILL_REPEATS);
|
||||
let mut s = first_line.clone();
|
||||
if first_line.is_char_boundary(pos) {
|
||||
s.insert_str(pos, &ch.to_string().repeat(n));
|
||||
|
||||
Reference in New Issue
Block a user