/* $Id: guppi-scatter-state.c,v 1.29 2002/01/21 02:30:28 jody Exp $ */
/*
* guppi-scatter-state.c
*
* Copyright (C) 1999, 2000 EMC Capital Management, Inc.
* Copyright (C) 2001 The Free Software Foundation
*
* Developed by Jon Trowbridge <trow@gnu.org> and
* Havoc Pennington <hp@pobox.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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include "guppi-scatter-state.h"
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>
#include <math.h>
#include <glade/glade.h>
#include <gtk/gtkmarshal.h>
#include <guppi-convenient.h>
#include <guppi-data-flavor.h>
#include <guppi-useful.h>
#include <guppi-data-select.h>
#include <guppi-data-socket.h>
#include "guppi-scatter-view.h"
static GtkObjectClass *parent_class = NULL;
static void
guppi_scatter_state_finalize (GtkObject * obj)
{
GuppiScatterState *ss = GUPPI_SCATTER_STATE (obj);
guppi_pixbuf_unref (ss->last_pixbuf);
if (parent_class->finalize)
parent_class->finalize (obj);
}
static GuppiElementView *
make_view (GuppiElementState * state)
{
return GUPPI_ELEMENT_VIEW (guppi_type_new (GUPPI_TYPE_SCATTER_VIEW));
}
static void
guppi_scatter_state_class_init (GuppiScatterStateClass * klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
GuppiElementStateClass *state_class = GUPPI_ELEMENT_STATE_CLASS (klass);
parent_class = gtk_type_class (GUPPI_TYPE_ELEMENT_STATE);
object_class->finalize = guppi_scatter_state_finalize;
state_class->name = _("Scatter Plot");
state_class->make_view = make_view;
}
static void
guppi_scatter_state_init (GuppiScatterState *ss)
{
GuppiAttributeBag *bag = guppi_element_state_attribute_bag (GUPPI_ELEMENT_STATE (ss));
/* FIXME: leaking */
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "x_data::socket::adopt", NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_SCALAR));
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "y_data::socket::adopt", NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_SCALAR));
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "data_mask::socket::adopt", NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_BOOLEAN));
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "data_color::socket::adopt", NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_SCALAR));
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "data_size1::socket::adopt", NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_SCALAR));
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "data_size2::socket::adopt", NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_SCALAR));
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_INT, "x_axis_type", NULL, GUPPI_AXIS_SCALAR);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_INT, "y_axis_type", NULL, GUPPI_AXIS_SCALAR);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA, "color", NULL, RGBA_RED);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_INT, "marker", NULL, GUPPI_MARKER_CIRCLE);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DOUBLE, "size1", NULL, 1.0);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN, "size1_use_gradient", NULL, TRUE);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN, "size1_reverse_gradient", NULL, FALSE);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DOUBLE, "size1_gradient_scale", NULL, 1.0);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DOUBLE, "size2", NULL, 1.0);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN, "size2_use_gradient", NULL, TRUE);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN, "size2_reverse_gradient", NULL, FALSE);
guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DOUBLE, "size2_gradient_scale", NULL, 1.0);
ss->last_marker = GUPPI_MARKER_UNKNOWN;
}
GtkType
guppi_scatter_state_get_type (void)
{
static GtkType gss_type = 0;
if (!gss_type) {
static const GtkTypeInfo gss_info = {
"GuppiScatterState",
sizeof (GuppiScatterState),
sizeof (GuppiScatterStateClass),
(GtkClassInitFunc) guppi_scatter_state_class_init,
(GtkObjectInitFunc) guppi_scatter_state_init,
NULL, NULL,
(GtkClassInitFunc) NULL
};
gss_type = gtk_type_unique (GUPPI_TYPE_ELEMENT_STATE, &gss_info);
}
return gss_type;
}
GuppiElementState *
guppi_scatter_state_new (void)
{
return GUPPI_ELEMENT_STATE (guppi_type_new (guppi_scatter_state_get_type ()));
}
GuppiSeqScalar *
guppi_scatter_state_get_x_data (GuppiScatterState *ss)
{
GuppiSeqScalar *d;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "x_data", &d, NULL);
guppi_unref (d);
return d;
}
GuppiSeqScalar *
guppi_scatter_state_get_y_data (GuppiScatterState *ss)
{
GuppiSeqScalar *d;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "y_data", &d, NULL);
guppi_unref (d);
return d;
}
int
guppi_scatter_state_get_x_axis_type (GuppiScatterState *ss)
{
int type;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "x_axis_type", &type, NULL);
return type;
}
int
guppi_scatter_state_get_y_axis_type (GuppiScatterState *ss)
{
int type;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "y_axis_type", &type, NULL);
return type;
}
GuppiSeqBoolean *
guppi_scatter_state_get_mask_data (GuppiScatterState *ss)
{
GuppiSeqBoolean *d;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "data_mask", &d, NULL);
guppi_unref (d);
return d;
}
GuppiSeqScalar *
guppi_scatter_state_get_color_data (GuppiScatterState *ss)
{
GuppiSeqScalar *d;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "data_color", &d, NULL);
guppi_unref (d);
return d;
}
GuppiSeqScalar *
guppi_scatter_state_get_size1_data (GuppiScatterState *ss)
{
GuppiSeqScalar *d;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "data_size1", &d, NULL);
guppi_unref (d);
return d;
}
GuppiSeqScalar *
guppi_scatter_state_get_size2_data (GuppiScatterState *ss)
{
GuppiSeqScalar *d;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "data_size2", &d, NULL);
guppi_unref (d);
return d;
}
void
guppi_scatter_state_set_x_data (GuppiScatterState *ss, GuppiSeqScalar *d)
{
g_return_if_fail (GUPPI_IS_SCATTER_STATE (ss));
g_return_if_fail (d == NULL || GUPPI_IS_SEQ_SCALAR (d));
guppi_element_state_set (GUPPI_ELEMENT_STATE (ss), "x_data", d, NULL);
}
void
guppi_scatter_state_set_y_data (GuppiScatterState *ss, GuppiSeqScalar *d)
{
g_return_if_fail (GUPPI_IS_SCATTER_STATE (ss));
g_return_if_fail (d == NULL || GUPPI_IS_SEQ_SCALAR (d));
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "y_data", d, NULL);
}
void
guppi_scatter_state_set_x_axis_type (GuppiScatterState *ss, int type)
{
g_return_if_fail (GUPPI_IS_SCATTER_STATE (ss));
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "x_axis_type", type, NULL);
}
void
guppi_scatter_state_set_y_axis_type (GuppiScatterState *ss, int type)
{
g_return_if_fail (GUPPI_IS_SCATTER_STATE (ss));
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "y_axis_type", type, NULL);
}
void
guppi_scatter_state_set_mask_data (GuppiScatterState *ss, GuppiSeqBoolean *d)
{
g_return_if_fail (GUPPI_IS_SCATTER_STATE (ss));
g_return_if_fail (d == NULL || GUPPI_IS_SEQ_BOOLEAN (d));
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "data_mask", d, NULL);
}
void
guppi_scatter_state_set_color_data (GuppiScatterState *ss, GuppiSeqScalar *d)
{
g_return_if_fail (GUPPI_IS_SCATTER_STATE (ss));
g_return_if_fail (d == NULL || GUPPI_IS_SEQ_SCALAR (d));
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "data_color", d, NULL);
}
void
guppi_scatter_state_set_size1_data (GuppiScatterState *ss, GuppiSeqScalar *d)
{
g_return_if_fail (GUPPI_IS_SCATTER_STATE (ss));
g_return_if_fail (d == NULL || GUPPI_IS_SEQ_SCALAR (d));
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "data_size1", d, NULL);
}
void
guppi_scatter_state_set_size2_data (GuppiScatterState *ss, GuppiSeqScalar *d)
{
g_return_if_fail (GUPPI_IS_SCATTER_STATE (ss));
g_return_if_fail (d == NULL || GUPPI_IS_SEQ_SCALAR (d));
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss), "data_size2", d, NULL);
}
gboolean
guppi_scatter_state_get_point_properties (GuppiScatterState *ss,
gint index,
gboolean *visible,
GuppiMarker *marker,
guint32 *color,
double *size1,
double *size2)
{
GuppiMarker our_marker;
GuppiSeqBoolean *mask;
GuppiSeqScalar *size1_data;
GuppiSeqScalar *size2_data;
const GuppiMarkerInfo *info;
gboolean use_sz1, use_sz2, rev_sz1, rev_sz2;
double sz1, sz2, scale_sz1, scale_sz2, a, b, t, x;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), FALSE);
if (visible) {
mask = guppi_scatter_state_get_mask_data (ss);
if (mask == NULL || ! guppi_seq_in_bounds (GUPPI_SEQ (mask), index))
*visible = TRUE;
else
*visible = ! guppi_seq_boolean_get (mask, index);
}
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss),
"marker", &our_marker,
"color", color,
NULL);
#if 0
our_marker = (GuppiMarker) (index % (gint) GUPPI_MARKER_LAST); /* FIXME */
#endif
if (marker)
*marker = our_marker;
info = guppi_marker_info (our_marker);
if (size1) {
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss),
"size1", &sz1,
"size1_use_gradient", &use_sz1,
"size1_reverse_gradient", &rev_sz1,
"size1_gradient_scale", &scale_sz1,
NULL);
sz1 *= info->size1_default;
if (use_sz1) {
size1_data = guppi_scatter_state_get_size1_data (ss);
if (size1_data) {
t = 0.5;
if (size1_data && guppi_seq_in_bounds (GUPPI_SEQ (size1_data), index)) {
a = guppi_seq_scalar_min (size1_data);
b = guppi_seq_scalar_max (size1_data);
if (a < b) {
x = guppi_seq_scalar_get (size1_data, index);
t = (x - a) / (b - a);
}
}
if (rev_sz1)
t = 1-t;
a = info->size1_min * scale_sz1;
b = info->size1_max * scale_sz1;
sz1 = a + t * (b - a);
}
}
#if 0
sz1 *= 10; /* FIXME */
#endif
*size1 = sz1;
}
if (size2) {
guppi_element_state_get (GUPPI_ELEMENT_STATE (ss),
"size2", &sz2,
"size2_use_gradient", &use_sz2,
"size2_reverse_gradient", &rev_sz2,
"size2_gradient_scale", &scale_sz2,
NULL);
sz2 *= info->size2_default;
if (use_sz2) {
size2_data = guppi_scatter_state_get_size2_data (ss);
if (size2_data) {
t = 0.5;
if (size2_data && guppi_seq_in_bounds (GUPPI_SEQ (size2_data), index)) {
a = guppi_seq_scalar_min (size2_data);
b = guppi_seq_scalar_max (size2_data);
if (a < b) {
x = guppi_seq_scalar_get (size2_data, index);
t = (x - a) / (b - a);
}
}
if (rev_sz2)
t = 1-t;
a = info->size2_min * scale_sz2;
b = info->size2_max * scale_sz2;
sz2 = a + t * (b - a);
}
}
#if 0
sz2 *= 10; /* FIXME */
#endif
*size2 = sz2;
}
return TRUE;
}
GuppiPixbuf *
guppi_scatter_state_get_point_pixbuf (GuppiScatterState *ss,
gint index,
double scale_factor,
guint32 *color)
{
gboolean visible;
GuppiMarker marker;
double size1, size2;
GuppiPixbuf *pixbuf;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), NULL);
g_return_val_if_fail (scale_factor > 0, NULL);
if (! guppi_scatter_state_get_point_properties (ss, index, &visible, &marker, color, &size1, &size2))
return NULL;
if (!visible)
return NULL;
/* Very inefficient. Only the minimum possible cacheing. */
if (marker == ss->last_marker
&& size1 == ss->last_size1
&& size2 == ss->last_size2
&& scale_factor == ss->last_scale) {
guppi_pixbuf_ref (ss->last_pixbuf);
pixbuf = ss->last_pixbuf;
} else {
pixbuf = guppi_marker_pixbuf (marker, size1, size2, scale_factor);
if (pixbuf != NULL) {
guppi_pixbuf_ref (pixbuf);
guppi_pixbuf_unref (ss->last_pixbuf);
ss->last_pixbuf = pixbuf;
ss->last_marker = marker;
ss->last_size1 = size1;
ss->last_size2 = size2;
ss->last_scale = scale_factor;
}
}
return pixbuf;
}
/* This could be optimized quite a bit. */
gboolean
guppi_scatter_state_closest_point (GuppiScatterState *ss,
double x, double y,
double r,
double x_scale, double y_scale,
gint *index)
{
gboolean hits = FALSE;
gint i, i0, i1, j0, j1, min_i = 0;
double xd, yd, dist, min_dist = 1e+12;
GuppiSeqScalar *x_data;
GuppiSeqScalar *y_data;
GuppiSeqBoolean *mask_data;
g_return_val_if_fail (GUPPI_IS_SCATTER_STATE (ss), FALSE);
g_return_val_if_fail (r >= 0, FALSE);
if (index == NULL)
return FALSE;
x_data = guppi_scatter_state_get_x_data (ss);
y_data = guppi_scatter_state_get_y_data (ss);
mask_data = guppi_scatter_state_get_mask_data (ss);
if (x_data == NULL || y_data == NULL)
return FALSE;
guppi_seq_common_bounds (GUPPI_SEQ (x_data), GUPPI_SEQ (y_data), &i0, &i1);
if (mask_data) {
guppi_seq_bounds (GUPPI_SEQ (mask_data), &j0, &j1);
} else {
j0 = 0;
j1 = -1;
}
for (i = i0; i <= i1; ++i) {
xd = guppi_seq_scalar_get (x_data, i) / x_scale;
yd = guppi_seq_scalar_get (y_data, i) / y_scale;
dist = xd * xd + yd * yd;
if (dist < min_dist && ((i < j0 || j1 < i) || !guppi_seq_boolean_get (mask_data, i))) {
min_i = i;
min_dist = dist;
hits = TRUE;
}
}
*index = min_i;
return hits;
}
void
guppi_scatter_state_brush_rectangle (GuppiScatterState *ss,
double x0, double y0,
double x1, double y1,
gboolean hidden)
{
GuppiSeqScalar *x_data;
GuppiSeqScalar *y_data;
GuppiSeqBoolean *mask_data;
gint i, i0, i1, j0, j1;
g_return_if_fail (ss != NULL);
x_data = guppi_scatter_state_get_x_data (ss);
y_data = guppi_scatter_state_get_y_data (ss);
mask_data = guppi_scatter_state_get_mask_data (ss);
if (x_data == NULL || y_data == NULL)
return;
if (mask_data == NULL) {
mask_data = GUPPI_SEQ_BOOLEAN (guppi_seq_boolean_new ());
guppi_scatter_state_set_mask_data (ss, mask_data);
}
guppi_seq_common_bounds (GUPPI_SEQ (x_data), GUPPI_SEQ (y_data), &i0, &i1);
guppi_seq_bounds (GUPPI_SEQ (mask_data), &j0, &j1);
for (i = i0; i <= i1; ++i) {
double x, y;
x = guppi_seq_scalar_get (x_data, i);
if (x0 <= x && x <= x1) {
y = guppi_seq_scalar_get (y_data, i);
if (y0 <= y && y <= y1) {
if (i < j0 || j1 < i) {
guppi_seq_grow_to_include (GUPPI_SEQ (mask_data), i);
guppi_seq_bounds (GUPPI_SEQ (mask_data), &j0, &j1);
}
guppi_seq_boolean_set (mask_data, i, hidden);
}
}
}
}
void
guppi_scatter_state_brush_circle (GuppiScatterState *ss,
double x, double y, double r,
double x_scale, double y_scale,
gboolean hidden)
{
GuppiSeqScalar *x_data;
GuppiSeqScalar *y_data;
GuppiSeqBoolean *mask_data;
gint i, i0, i1, j0, j1;
double rr, d;
g_return_if_fail (ss != NULL);
x_data = guppi_scatter_state_get_x_data (ss);
y_data = guppi_scatter_state_get_y_data (ss);
mask_data = guppi_scatter_state_get_mask_data (ss);
if (x_data == NULL || y_data == NULL)
return;
if (mask_data == NULL) {
mask_data = GUPPI_SEQ_BOOLEAN (guppi_seq_boolean_new ());
guppi_scatter_state_set_mask_data (ss, mask_data);
}
r = fabs (r);
rr = r * r;
guppi_seq_common_bounds (GUPPI_SEQ (x_data), GUPPI_SEQ (y_data), &i0, &i1);
guppi_seq_bounds (GUPPI_SEQ (mask_data), &j0, &j1);
for (i = i0; i <= i1; ++i) {
double xx, yy;
xx = (guppi_seq_scalar_get (x_data, i) - x) / x_scale;
if (-r <= xx && xx <= r) {
yy = (guppi_seq_scalar_get (y_data, i) - y) / y_scale;
if (-r <= yy && yy <= r) {
d = xx * xx + yy * yy;
if (d <= rr) {
if (hidden && (i < j0 || j1 < i)) {
guppi_seq_grow_to_include (GUPPI_SEQ (mask_data), i);
guppi_seq_bounds (GUPPI_SEQ (mask_data), &j0, &j1);
}
if (j0 <= i && i <= j1) {
guppi_seq_boolean_set (mask_data, i, hidden);
}
}
}
}
}
}
/* $Id: guppi-scatter-state.c,v 1.29 2002/01/21 02:30:28 jody Exp $ */
syntax highlighted by Code2HTML, v. 0.9.1