Files
sysprof/src/libsysprof-analyze/sysprof-xy-series.c
Christian Hergert d0d19cff39 libsysprof-analyze: use doubles for reference data
We only want to move to float for the internal 0..1 positions which are
used to draw to the screen (to reduce how much data we hold on to). But for
the data we need to calculate those 0..1 positions, we want better
precision for large numbers. Use double for all of those.
2023-06-20 11:49:22 -07:00

222 lines
5.5 KiB
C

/* sysprof-xy-series.c
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include <math.h>
#include "sysprof-xy-series.h"
struct _SysprofXYSeries
{
/* Model of SysprofDocumentFrame */
GListModel *model;
/* Array of SysprofXYSeriesValue */
GArray *values;
/* Our bounds for non-normalized values */
double min_x;
double min_y;
double max_x;
double max_y;
/* Pre-calculated distance between min/max */
double x_distance;
double y_distance;
/* Used for warning on items-changed */
gulong items_changed_handler;
};
G_DEFINE_BOXED_TYPE (SysprofXYSeries,
sysprof_xy_series,
sysprof_xy_series_ref,
sysprof_xy_series_unref)
static void
warn_on_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
gpointer user_data)
{
g_critical ("%s @ %p emitted items changed while an XYSeries is active! Expect errors.",
G_OBJECT_TYPE_NAME (model), model);
}
/**
* sysprof_xy_series_new:
* @model: a #GListModel
* @xy_span: the span of the xy series
*
* Used for creating normalized xy spans (between 0..1) for xys
* within a xyspan. Useful for creating xy-based charts at any
* size or scale.
*
* It is required that @model does not change during the lifexy
* of the xy series.
*/
SysprofXYSeries *
sysprof_xy_series_new (GListModel *model,
double min_x,
double min_y,
double max_x,
double max_y)
{
SysprofXYSeries *self;
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
self = g_atomic_rc_box_new0 (SysprofXYSeries);
self->model = g_object_ref (model);
self->values = g_array_new (FALSE, FALSE, sizeof (SysprofXYSeriesValue));
self->min_x = min_x;
self->min_y = min_y;
self->max_x = max_x;
self->max_y = max_y;
self->x_distance = max_x - min_x;
self->y_distance = max_y - min_y;
self->items_changed_handler =
g_signal_connect (self->model,
"items-changed",
G_CALLBACK (warn_on_items_changed_cb),
NULL);
return self;
}
SysprofXYSeries *
sysprof_xy_series_ref (SysprofXYSeries *self)
{
return g_atomic_rc_box_acquire (self);
}
static void
_sysprof_xy_series_finalize (SysprofXYSeries *self)
{
g_clear_signal_handler (&self->items_changed_handler, self->model);
g_clear_object (&self->model);
g_clear_pointer (&self->values, g_array_unref);
}
void
sysprof_xy_series_unref (SysprofXYSeries *self)
{
g_atomic_rc_box_release_full (self,
(GDestroyNotify)_sysprof_xy_series_finalize);
}
void
sysprof_xy_series_add (SysprofXYSeries *self,
double x,
double y,
guint index)
{
SysprofXYSeriesValue value;
if (x < self->min_x || x > self->max_x)
return;
if (y < self->min_y || y > self->max_y)
return;
value.x = (x - self->min_x) / self->x_distance;
value.y = (y - self->min_y) / self->y_distance;
value.index = index;
g_array_append_val (self->values, value);
}
/**
* sysprof_xy_series_get_model:
* @self: a #SysprofXYSeries
*
* Gets the underlying model for the xy series.
*
* Returns: (transfer none): a #GListModel
*/
GListModel *
sysprof_xy_series_get_model (SysprofXYSeries *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->model;
}
const SysprofXYSeriesValue *
sysprof_xy_series_get_values (const SysprofXYSeries *self,
guint *n_values)
{
*n_values = self->values->len;
return &g_array_index (self->values, SysprofXYSeriesValue, 0);
}
static int
compare_by_xy (gconstpointer aptr,
gconstpointer bptr)
{
const SysprofXYSeriesValue *a = aptr;
const SysprofXYSeriesValue *b = bptr;
if (a->x < b->x)
return -1;
else if (a->x > b->x)
return 1;
if (a->y < b->y)
return -1;
else if (a->y > b->y)
return 1;
return 0;
}
void
sysprof_xy_series_sort (SysprofXYSeries *self)
{
qsort (self->values->data,
self->values->len,
sizeof (SysprofXYSeriesValue),
compare_by_xy);
}
void
sysprof_xy_series_get_range (SysprofXYSeries *self,
double *min_x,
double *min_y,
double *max_x,
double *max_y)
{
g_return_if_fail (self != NULL);
if (min_x)
*min_x = self->min_x;
if (max_x)
*max_x = self->max_x;
if (min_y)
*min_y = self->min_y;
if (max_y)
*max_y = self->max_y;
}