/* $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 and * Havoc Pennington . * * 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 #include "guppi-scatter-state.h" #include #include #include #include #include #include #include #include #include #include #include #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 $ */