/* This is -*- C -*- */
/* vim: set sw=2: */
/* $Id: guppi-curve-interpolate.c,v 1.1 2002/01/08 06:28:59 trow Exp $ */

/*
 * guppi-curve-interpolate-impl.c
 *
 * Copyright (C) 2001 The Free Software Foundation
 *
 * Developed by Jon Trowbridge <trow@gnu.org>
 *
 * 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-curve-interpolate.h"

#include <math.h>
#include <guppi-memory.h>
#include <guppi-data-plug-in.h>
#include <guppi-seq-scalar.h>

static GtkObjectClass *parent_class = NULL;

enum {
  ARG_0,
  ARG_X_DATA,
  ARG_Y_DATA
};

static gboolean allowed_data_type      (GuppiSeq *);
static void     interpolate_set_x_data (GuppiCurveInterpolate *, GuppiSeq *);
static void     interpolate_set_y_data (GuppiCurveInterpolate *, GuppiSeq *);

static void
guppi_curve_interpolate_get_arg (GtkObject *obj, GtkArg *arg, guint arg_id)
{
  GuppiCurveInterpolate *interp = GUPPI_CURVE_INTERPOLATE (obj);

  switch (arg_id) {

  case ARG_X_DATA:
    GTK_VALUE_POINTER (*arg) = interp->x_data;
    break;

  case ARG_Y_DATA:
    GTK_VALUE_POINTER (*arg) = interp->y_data;
    break;

  default:
    break;
  };
}

static void
guppi_curve_interpolate_set_arg (GtkObject *obj, GtkArg *arg, guint arg_id)
{
  GuppiCurveInterpolate *interp = GUPPI_CURVE_INTERPOLATE (obj);

  switch (arg_id) {

  case ARG_X_DATA:
    interpolate_set_x_data (interp, GUPPI_SEQ (GTK_VALUE_POINTER (*arg)));
    break;

  case ARG_Y_DATA:
    interpolate_set_y_data (interp, GUPPI_SEQ (GTK_VALUE_POINTER (*arg)));
    break;

  default:
    break;
  };
}

static void
guppi_curve_interpolate_finalize (GtkObject *obj)
{
  GuppiCurveInterpolate *interp = GUPPI_CURVE_INTERPOLATE(obj);

  guppi_unref (interp->x_data);
  guppi_unref (interp->y_data);

  guppi_finalized (obj);

  if (parent_class->finalize)
    parent_class->finalize (obj);
}

/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static void
bounds (GuppiCurve *curve, double *a, double *b)
{
  GuppiCurveInterpolate *interp = GUPPI_CURVE_INTERPOLATE (curve);
  gint min_bound, max_bound;

  if (interp->x_data && interp->y_data) {
    guppi_seq_common_bounds (interp->x_data, interp->y_data, &min_bound, &max_bound);
  } else {
    min_bound = max_bound = 0;
  }

  if (a) *a = min_bound;
  if (b) *b = max_bound;
}

static double
lookup_value (GuppiSeq *seq, gint i)
{
  if (GUPPI_IS_SEQ_SCALAR (seq)) {
    return guppi_seq_scalar_get ((GuppiSeqScalar *) seq, i);
  }

  return 0;
}

static void
get (GuppiCurve *curve, double t, double *x, double *y)
{
  GuppiCurveInterpolate *interp = GUPPI_CURVE_INTERPOLATE (curve);
  double xx, yy;

  if (interp->x_data && interp->y_data) {
    gint t_int    = (gint)floor (t);
    double t_frac = t - t_int;
    double x0, y0, x1, y1;

    if (t_frac < 1e-8) {
      xx = lookup_value (interp->x_data, t_int);
      yy = lookup_value (interp->y_data, t_int);
    } else {
      x0 = lookup_value (interp->x_data, t_int);
      x1 = lookup_value (interp->x_data, t_int+1);
      y0 = lookup_value (interp->y_data, t_int);
      y1 = lookup_value (interp->y_data, t_int+1);
      xx = (1-t_frac) * x0 + t_frac * x1;
      yy = (1-t_frac) * y0 + t_frac * y1;
    }
    
  } else {
    xx = 0;
    yy = 0;
  }

  if (x) *x = xx;
  if (y) *y = yy;
}

static void
bbox (GuppiCurve *curve, double t0, double t1,
      double *x0, double *y0, double *x1, double *y1)
{
  GuppiCurveInterpolate *interp = GUPPI_CURVE_INTERPOLATE (curve);
  double mx, Mx, my, My, x, y;
  gint i, i0, i1;

  i0 = (gint) ceil (t0);
  i1 = (gint) floor (t1);

  if (interp->x_data && interp->y_data) {

    get (curve, t0, &mx, &my);
    Mx = mx, My = my;

    for (i = i0; i <= i1; ++i) {
      x = lookup_value (interp->x_data, i);
      if (x < mx) mx = x;
      if (x > Mx) Mx = x;
      y = lookup_value (interp->y_data, i);
      if (y < my) my = y;
      if (y > My) My = y;
    }

    get (curve, t1, &x, &y);
    if (x < mx) mx = x;
    if (x > Mx) Mx = x;
    if (y < my) my = y;
    if (y > My) My = y;
      
  } else {
    mx = my = Mx = My = 0;
  }

  if (x0) *x0 = mx;
  if (x1) *x1 = Mx;
  if (y0) *y0 = my;
  if (y1) *y1 = My;
}

static ArtVpath *
approx_to_path (GuppiCurve *curve, double t0, double t1,
		double x_error, double y_error,
		double x0, double y0, double x1, double y1,
		double scale_x, double scale_y)
{
  GuppiCurveInterpolate *interp = GUPPI_CURVE_INTERPOLATE (curve);
  gint i, i0, i1, N, j = 0;
  double x, y, xp, yp;
  gboolean ok;
  ArtVpath *path = NULL;

  if (interp->x_data == NULL || interp->y_data == NULL)
    return NULL;

  ok = TRUE;
  
  i0 = (gint) ceil (t0);
  i1 = (gint) floor (t1);

  N = i1 - i0 + 4;
  path = g_new0 (ArtVpath, N);
  
  /* First point */
  path[j].code = ART_MOVETO;
  get (curve, t0, &path[j].x, &path[j].y);
  ++j;
  
  for (i = i0; i <= i1; ++i) {
    x = lookup_value (interp->x_data, i);
    y = lookup_value (interp->y_data, i);
    
    if (j > 0) {
      xp = path[j-1].x;
      yp = path[j-1].y;
      
      if (ok && fabs (scale_x * (x - xp)) < 0.5 && fabs (scale_y * (y - yp)) < 0.5)
	ok = FALSE;
      
      /* Need to do window queries */
      /* Need to do colinearity checks */
    }
    
    if (ok) {
      path[j].code = ART_LINETO;
      path[j].x = x;
      path[j].y = y;
      ++j;
    }
    
  }
  
  path[j].code = ART_END;
  
  return path;
}

/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static void
guppi_curve_interpolate_class_init (GuppiCurveInterpolateClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *)klass;
  GuppiCurveClass *curve_class = GUPPI_CURVE_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_CURVE);

  object_class->get_arg = guppi_curve_interpolate_get_arg;
  object_class->set_arg = guppi_curve_interpolate_set_arg;
  object_class->finalize = guppi_curve_interpolate_finalize;

  curve_class->bounds         = bounds;
  curve_class->get            = get;
  curve_class->bbox           = bbox;
  curve_class->approx_to_path = approx_to_path;
}

static void
guppi_curve_interpolate_init (GuppiCurveInterpolate *obj)
{

}

GtkType
guppi_curve_interpolate_get_type (void)
{
  static GtkType guppi_curve_interpolate_type = 0;
  if (!guppi_curve_interpolate_type) {
    static const GtkTypeInfo guppi_curve_interpolate_info = {
      "GuppiCurveInterpolate",
      sizeof (GuppiCurveInterpolate),
      sizeof (GuppiCurveInterpolateClass),
      (GtkClassInitFunc)guppi_curve_interpolate_class_init,
      (GtkObjectInitFunc)guppi_curve_interpolate_init,
      NULL, NULL, (GtkClassInitFunc)NULL
    };
    guppi_curve_interpolate_type = gtk_type_unique (GUPPI_TYPE_CURVE, &guppi_curve_interpolate_info);
  }
  return guppi_curve_interpolate_type;
}

/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static gboolean
allowed_data_type (GuppiSeq *seq)
{
  if (seq == NULL)
    return TRUE;

  if (GUPPI_IS_SEQ_SCALAR (seq))
    return TRUE;

  return FALSE;
}

static void
interpolate_set_x_data (GuppiCurveInterpolate *interp, GuppiSeq *data)
{
  g_return_if_fail (GUPPI_IS_CURVE_INTERPOLATE (interp));
  g_return_if_fail (allowed_data_type (data));

  if (interp->x_data != data) {
    if (interp->changed_x_handler)
      gtk_signal_disconnect (GTK_OBJECT (interp->x_data), interp->changed_x_handler);
    guppi_refcounting_assign (interp->x_data, data);
    if (data)
      interp->changed_x_handler = gtk_signal_connect_object (GTK_OBJECT (data),
							     "changed",
							     GTK_SIGNAL_FUNC (guppi_data_changed),
							     GTK_OBJECT (interp));
    guppi_data_changed (GUPPI_DATA (interp));
  }
}

static void
interpolate_set_y_data (GuppiCurveInterpolate *interp, GuppiSeq *data)
{
  g_return_if_fail (GUPPI_IS_CURVE_INTERPOLATE (interp));
  g_return_if_fail (allowed_data_type (data));

  if (interp->y_data != data) {
    if (interp->changed_y_handler)
      gtk_signal_disconnect (GTK_OBJECT (interp->y_data), interp->changed_y_handler);
    guppi_refcounting_assign (interp->y_data, data);
    if (data)
      interp->changed_y_handler = gtk_signal_connect_object (GTK_OBJECT (data),
							     "changed",
							     GTK_SIGNAL_FUNC (guppi_data_changed),
							     GTK_OBJECT (interp));
    guppi_data_changed (GUPPI_DATA (interp));
  }
}


syntax highlighted by Code2HTML, v. 0.9.1