/* $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