[goffice] Graphs: improve date axes.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [goffice] Graphs: improve date axes.
- Date: Mon, 30 Aug 2010 23:27:03 +0000 (UTC)
commit a3925e25433b4347ab65b65f3ffad8ca3ca5f0ef
Author: Morten Welinder <terra gnome org>
Date: Mon Aug 30 19:26:45 2010 -0400
Graphs: improve date axes.
ChangeLog | 3 +
NEWS | 1 +
goffice/graph/gog-axis.c | 283 +++++++++++++++++++++++-----------------------
3 files changed, 148 insertions(+), 139 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 82cc45b..9631db9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
2010-08-30 Morten Welinder <terra gnome org>
+ * goffice/graph/gog-axis.c (map_date_calc_ticks): Simplify and
+ handle non-integer steps.
+
* goffice/utils/go-format.c (go_format_month_before_day): Fix test
for date format.
(go_format_is_date): Return also an indication of whether the
diff --git a/NEWS b/NEWS
index 6733868..a86aaf5 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,7 @@ Morten:
* Fix char-xmlChar confusion.
* Fix graph crash. [#628259]
* Improve go_format_is_date.
+ * Improve handling of date axes with time-of-day.
--------------------------------------------------------------------------
goffice 0.8.9:
diff --git a/goffice/graph/gog-axis.c b/goffice/graph/gog-axis.c
index 0b7d47a..3b6aa69 100644
--- a/goffice/graph/gog-axis.c
+++ b/goffice/graph/gog-axis.c
@@ -775,113 +775,89 @@ map_linear_calc_ticks (GogAxis *axis)
gog_axis_set_ticks (axis, t, ticks);
}
-static gboolean
-add_days (GDate *d, int n)
-{
- int m = 23936166 - g_date_get_julian (d); /* 31-Dec-65535 */
-
- if (n > m)
- return FALSE;
+static const int j_horizon = 23936166; /* 31-Dec-65535 */
- g_date_add_days (d, n);
- return TRUE;
-}
+typedef struct {
+ enum { DS_MONTHS, DS_DAYS } unit;
+ double n;
+} DateStep;
+/*
+ * Moral equivalent of "res = start + i * step".
+ *
+ * Note the date system: (GDate's) Julian day + fractional day.
+ */
static gboolean
-add_months (GDate *d, int n)
+dt_add (double *res, double start, double i, const DateStep *step)
{
- int m = (65535 - g_date_get_year (d)) * 12 +
- (12 - g_date_get_month (d));
+ if (start <= 0 && start > j_horizon)
+ return FALSE;
- if (n > m)
+ switch (step->unit) {
+ case DS_DAYS:
+ *res = start + i * step->n;
+ break;
+ case DS_MONTHS: {
+ GDate d;
+ int istart = (int)start;
+ int m;
+ double n = i * step->n;
+
+ if (n < 0 || n != floor (n))
+ return FALSE;
+
+ g_date_clear (&d, 1);
+ g_date_set_julian (&d, istart);
+ m = (65535 - g_date_get_year (&d)) * 12 +
+ (12 - g_date_get_month (&d));
+ if (n > m)
+ return FALSE;
+ g_date_add_months (&d, (int)n);
+
+ *res = g_date_get_julian (&d) + (start - istart);
+ break;
+ }
+ default:
return FALSE;
+ }
- g_date_add_months (d, n);
- return TRUE;
+ return *res > 0 && *res <= j_horizon;
}
-static void
-map_week_calc_ticks (GogAxis *axis)
+/*
+ * Calculate a lower bound to the number of days in the step. Accuracy is
+ * not terribly important.
+ */
+static double
+dt_inflen (const DateStep *step)
{
- GogAxisTick *ticks;
- double major_tick, minor_tick;
- double minimum, maximum, range;
- GDate min_date;
- int t, N, maj_i, min_i, maj_N, min_N;
- int min_days, maj_days;
-
- if (!gog_axis_get_bounds (axis, &minimum, &maximum) ||
- split_date (axis, minimum, &min_date))
- return; /* Shouldn't happen. */
- range = maximum - minimum;
-
- major_tick = gog_axis_get_entry (axis, GOG_AXIS_ELEM_MAJOR_TICK, NULL);
- minor_tick = gog_axis_get_entry (axis, GOG_AXIS_ELEM_MINOR_TICK, NULL);
-
- maj_days = (int)major_tick / 7 * 7;
- maj_N = (int)(range / 7);
-
- minor_tick = MIN (minor_tick, major_tick);
- min_days = minor_tick < major_tick ? 1 : (int)minor_tick / 7 * 7;
- min_N = maj_days / min_days;
-
- while (1) {
- double Nd = (maj_N + 1.0) * min_N + 1;
- if (Nd <= GOG_AXIS_MAX_TICK_NBR) {
- N = (int)Nd;
- break;
- }
-
- /* Too many. Now what? */
- if (min_N > 1) {
- /* Drop minor ticks. */
- min_N = 1;
- } else {
- /* Brutal. */
- maj_days *= maj_N;
- maj_N = 1;
- }
+ switch (step->unit) {
+ case DS_DAYS:
+ return step->n;
+ case DS_MONTHS:
+ return step->n * 28;
+ default:
+ return go_pinf;
}
+}
- ticks = g_new0 (GogAxisTick, N);
-
- t = 0;
- for (maj_i = 0; t < N; maj_i++) {
- GDate maj_d = min_date;
- double maj_pos;
-
- if (!add_days (&maj_d, maj_i * maj_days))
- break;
-
- maj_pos = go_date_g_to_serial (&maj_d, axis->date_conv);
- if (maj_pos > maximum)
- break;
-
- ticks[t].position = maj_pos;
- ticks[t].type = GOG_AXIS_TICK_MAJOR;
- ticks[t].label = axis_format_value (axis, maj_pos);
- t++;
- for (min_i = 1; min_i < min_N; min_i++) {
- GDate min_d = maj_d;
- double min_pos;
+static double
+dt_serial (double dt, const GODateConventions *date_conv)
+{
+ double dt0 = go_fake_floor (dt);
- if (!add_days (&min_d, min_i * min_days))
- break;
+ if (dt0 > 0 && dt < j_horizon) {
+ GDate d;
+ int s;
- min_pos = go_date_g_to_serial (&min_d, axis->date_conv);
- if (min_pos > maximum)
- break;
-
- g_assert (t < N);
- ticks[t].position = min_pos;
- ticks[t].type = GOG_AXIS_TICK_MINOR;
- ticks[t].label = NULL;
- t++;
- }
- }
+ g_date_clear (&d, 1);
+ g_date_set_julian (&d, (int)dt0);
- gog_axis_set_ticks (axis, t, ticks);
+ s = go_date_g_to_serial (&d, date_conv);
+ return s + (dt - dt0);
+ } else
+ return go_pinf;
}
static void
@@ -890,11 +866,11 @@ map_date_calc_ticks (GogAxis *axis)
GogAxisTick *ticks;
double major_tick, minor_tick;
double minimum, maximum, range, maj_months, min_months;
- int t, N, maj_i, min_i, maj_N, min_N;
GDate min_date, max_date;
- gboolean minor_is_days;
- int min_days = 0;
- gboolean major_is_weeks;
+ DateStep major_step, minor_step;
+ double start;
+ double maj_extra;
+ int N, t, maj_i;
if (!gog_axis_get_bounds (axis, &minimum, &maximum) ||
split_date (axis, minimum, &min_date) ||
@@ -907,56 +883,93 @@ map_date_calc_ticks (GogAxis *axis)
major_tick = gog_axis_get_entry (axis, GOG_AXIS_ELEM_MAJOR_TICK, NULL);
minor_tick = gog_axis_get_entry (axis, GOG_AXIS_ELEM_MINOR_TICK, NULL);
- major_is_weeks = (major_tick >= 7 && major_tick <= 28 &&
- major_tick == floor (major_tick) &&
- (int)major_tick % 7 == 0);
- if (major_is_weeks) {
- map_week_calc_ticks (axis);
- return;
- }
+ major_tick = MAX (major_tick, 0.0);
+ minor_tick = CLAMP (minor_tick, 0.0, major_tick);
maj_months = go_fake_round (major_tick / (DAYS_IN_YEAR / 12));
- maj_months = CLAMP (maj_months, 1, G_MAXINT / 12);
- maj_N = 1 + (int)ceil (range / (maj_months * (DAYS_IN_YEAR / 12)));
+ maj_months = CLAMP (maj_months, 0, G_MAXINT / 12);
+ if (maj_months == 0) {
+ major_step.unit = DS_DAYS;
+ major_step.n = major_tick;
+ } else {
+ major_step.unit = DS_MONTHS;
+ major_step.n = maj_months;
+ }
min_months = go_fake_round (minor_tick / (DAYS_IN_YEAR / 12));
min_months = CLAMP (min_months, 0, maj_months);
- minor_is_days = (min_months == 0);
- if (minor_is_days) {
- min_days = (int)CLAMP (minor_tick, 1, 31);
- min_N = 31 * maj_months / min_days;
- } else
- min_N = (int)(maj_months / min_months);
+ if (min_months == 0) {
+ minor_step.unit = DS_DAYS;
+ minor_step.n = minor_tick;
+ } else {
+ minor_step.unit = DS_MONTHS;
+ minor_step.n = min_months;
+ }
N = 0;
while (1) {
- double Nd = (double)maj_N * min_N + 1;
+ double maj_N = range / dt_inflen (&major_step);
+ double min_N = range / dt_inflen (&minor_step);
+ double Nd = maj_N + min_N + 1;
+
if (Nd <= GOG_AXIS_MAX_TICK_NBR) {
N = (int)Nd;
break;
}
/* Too many. Now what? */
- if (min_N > 1) {
+ if (min_N >= 2) {
/* Drop minor ticks. */
- min_N = 1;
+ minor_step.n = go_pinf;
} else {
- /* Brutal. */
- maj_months *= maj_N;
- maj_N = 1;
+ switch (major_step.unit) {
+ case DS_MONTHS:
+ if (major_step.n < 12)
+ major_step.n = 12;
+ else
+ major_step.n *= 10;
+ break;
+ case DS_DAYS:
+ if (major_step.n < 1)
+ major_step.n = 1;
+ else {
+ major_step.unit = DS_MONTHS;
+ major_step.n = 1;
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ major_step.n = go_pinf;
}
}
+ start = g_date_get_julian (&min_date) +
+ (minimum - go_date_g_to_serial (&min_date, axis->date_conv));
+
+ switch (major_step.unit) {
+ case DS_MONTHS:
+ maj_extra = 1;
+ break;
+ case DS_DAYS:
+ maj_extra = 1 - 0.5 / N;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
ticks = g_new0 (GogAxisTick, N);
t = 0;
for (maj_i = 0; t < N; maj_i++) {
- GDate maj_d = min_date;
- double maj_pos;
+ double maj_d, maj_dp1;
+ double maj_pos, maj_posp1, min_limit;
+ int min_i;
- if (!add_months (&maj_d, maj_i * (int)maj_months))
+ if (!dt_add (&maj_d, start, maj_i, &major_step))
break;
- maj_pos = go_date_g_to_serial (&maj_d, axis->date_conv);
+ maj_pos = dt_serial (maj_d, axis->date_conv);
if (maj_pos > maximum)
break;
@@ -965,29 +978,21 @@ map_date_calc_ticks (GogAxis *axis)
ticks[t].label = axis_format_value (axis, maj_pos);
t++;
- if (maj_i == maj_N)
+ /* Calculate next major so we know when to stop minors. */
+ if (!dt_add (&maj_dp1, start, maj_i + maj_extra, &major_step))
break;
+ maj_posp1 = dt_serial (maj_dp1, axis->date_conv);
+ min_limit = MIN (maximum, maj_posp1);
- for (min_i = 1; min_i < min_N; min_i++) {
- GDate min_d = maj_d;
+ for (min_i = 1; t < N; min_i++) {
+ double min_d;
double min_pos;
- if (minor_is_days) {
- int mths;
- if (!add_days (&min_d, min_i * min_days))
- break;
- mths = 12 * (g_date_get_year (&min_d) -
- g_date_get_year (&maj_d)) +
- (g_date_get_month (&min_d) -
- g_date_get_month (&maj_d));
- if (mths >= (int)maj_months)
- break;
- } else {
- if (!add_months (&min_d, min_i * (int)min_months))
- break;
- }
- min_pos = go_date_g_to_serial (&min_d, axis->date_conv);
- if (min_pos > maximum)
+ if (!dt_add (&min_d, maj_d, min_i, &minor_step))
+ break;
+
+ min_pos = dt_serial (min_d, axis->date_conv);
+ if (min_pos >= min_limit)
break;
ticks[t].position = min_pos;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]