/* This is -*- C -*- */
/* $Id: guppi-curve-func.c,v 1.1 2002/01/08 06:28:59 trow Exp $ */

/*
 * guppi-curve-func-impl.c
 *
 * Copyright (C) 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-curve-func.h"

#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>

#include <guppi-useful.h>
#include <guppi-memory.h>
#include <guppi-data-plug-in.h>

static GtkObjectClass *parent_class = NULL;

enum {
  ARG_0,
  ARG_CURVE,
  ARG_REPARAMETERIZE,
  ARG_T0,
  ARG_T1,
  ARG_T_FUNC,
  ARG_X_FUNC,
  ARG_Y_FUNC,
  ARG_X_BINARY_FUNC,
  ARG_Y_BINARY_FUNC,
  ARG_USE_2x2_MATRIX,
  ARG_MATRIX_A,
  ARG_MATRIX_B,
  ARG_MATRIX_C,
  ARG_MATRIX_D
};

static void
guppi_curve_func_get_arg (GtkObject *obj, GtkArg *arg, guint arg_id)
{
  switch (arg_id) {

  default:
    break;
  };
}

static void
guppi_curve_func_set_arg (GtkObject *obj, GtkArg *arg, guint arg_id)
{
  GuppiCurveFunc *func = GUPPI_CURVE_FUNC (obj);
  GuppiCurve *c;
  gboolean rp;
  double t;
  GuppiFnWrapper *fw;

  switch (arg_id) {

  case ARG_CURVE:
    c = GUPPI_CURVE (GTK_VALUE_POINTER (*arg));
    if (func->base_curve != c) {
      guppi_refcounting_assign (func->base_curve, c);
      guppi_data_changed (GUPPI_DATA (obj));
    };
    break;

  case ARG_REPARAMETERIZE:
    rp = GTK_VALUE_BOOL (*arg);
    if (func->reparameterize != rp) {
      func->reparameterize = rp;
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_T0:
    t = GTK_VALUE_DOUBLE (*arg);
    if (func->t0 != t || !func->reparameterize) {
      func->t0 = t;
      func->reparameterize = TRUE;
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_T1:
    t = GTK_VALUE_DOUBLE (*arg);
    if (func->t1 != t || !func->reparameterize) {
      func->t1 = t;
      func->reparameterize = TRUE;
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_T_FUNC:
    fw = GUPPI_FN_WRAPPER (GTK_VALUE_POINTER (*arg));
    if (func->t_fn_wrapper != fw || !func->reparameterize) {
      func->reparameterize = TRUE;
      guppi_refcounting_assign (func->t_fn_wrapper, fw);
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_X_FUNC:
    fw = GUPPI_FN_WRAPPER (GTK_VALUE_POINTER (*arg));
    if (func->x_fn_wrapper != fw) {
      guppi_refcounting_assign (func->x_fn_wrapper, fw);
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_Y_FUNC:
    fw = GUPPI_FN_WRAPPER (GTK_VALUE_POINTER (*arg));
    if (func->y_fn_wrapper != fw) {
      guppi_refcounting_assign (func->y_fn_wrapper, fw);
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_X_BINARY_FUNC:
    fw = GUPPI_FN_WRAPPER (GTK_VALUE_POINTER (*arg));
    if (func->x_binary_fn_wrapper != fw) {
      guppi_refcounting_assign (func->x_binary_fn_wrapper, fw);
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_Y_BINARY_FUNC:
    fw = GUPPI_FN_WRAPPER (GTK_VALUE_POINTER (*arg));
    if (func->y_binary_fn_wrapper != fw) {
      guppi_refcounting_assign (func->y_binary_fn_wrapper, fw);
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_USE_2x2_MATRIX:
    rp = GTK_VALUE_BOOL (*arg);
    if (func->use_2x2_matrix != rp) {
      func->use_2x2_matrix = rp;
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_MATRIX_A:
    t = GTK_VALUE_DOUBLE (*arg);
    if (func->a != t) {
      func->a = t;
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_MATRIX_B:
    t = GTK_VALUE_DOUBLE (*arg);
    if (func->b != t) {
      func->b = t;
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_MATRIX_C:
    t = GTK_VALUE_DOUBLE (*arg);
    if (func->c != t) {
      func->c = t;
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;

  case ARG_MATRIX_D:
    t = GTK_VALUE_DOUBLE (*arg);
    if (func->d != t) {
      func->d = t;
      guppi_data_changed (GUPPI_DATA (obj));
    }
    break;


  default:
    break;
  };
}

static void
guppi_curve_func_finalize (GtkObject *obj)
{
  GuppiCurveFunc *func = GUPPI_CURVE_FUNC (obj);

  guppi_unref0 (func->t_fn_wrapper);
  guppi_unref0 (func->x_fn_wrapper);
  guppi_unref0 (func->y_fn_wrapper);
  guppi_unref0 (func->x_binary_fn_wrapper);
  guppi_unref0 (func->y_binary_fn_wrapper);
  guppi_unref0 (func->base_curve);

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

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

static void
v_curve_bounds (GuppiCurve *curve, double *a, double *b)
{
  GuppiCurveFunc *cfi = GUPPI_CURVE_FUNC (curve);

  if (cfi->reparameterize) {

  if (a)
    *a = cfi->t0;

  if (b)
    *b = cfi->t1;

  } else {
    guppi_curve_parameter_bounds (cfi->base_curve, a, b);
  }
}

static void
v_curve_get (GuppiCurve *curve, double t, double *x, double *y)
{
  GuppiCurveFunc *cfi = GUPPI_CURVE_FUNC (curve);
  double xx, yy;

  if (cfi->reparameterize) {
    if (cfi->t_fn_wrapper) {
      t = guppi_fn_wrapper_eval_d__d (cfi->t_fn_wrapper, t);
      t = guppi_curve_clamp_parameter (cfi->base_curve, t);
    } else {
      double a, b, u;
      guppi_curve_parameter_bounds (cfi->base_curve, &a, &b);
      u = (t - cfi->t0)/(cfi->t1 - cfi->t0);
      t = a * u + b * (1-u);
    }
  }

  guppi_curve_get (cfi->base_curve, t, &xx, &yy);

  if (cfi->use_2x2_matrix) {
    if (x) *x = cfi->a * xx + cfi->b * yy;
    if (y) *y = cfi->c * xx + cfi->d * yy;
    return;
  }

  if (x) {
    if (cfi->x_binary_fn_wrapper)
      *x = guppi_fn_wrapper_eval_d__d_d (cfi->x_binary_fn_wrapper, xx, yy);
    else if (cfi->x_fn_wrapper)
      *x = guppi_fn_wrapper_eval_d__d (cfi->x_fn_wrapper, xx);
    else /* default is identity fn. */
      *x = xx;
  }

  if (y) {
    if (cfi->y_binary_fn_wrapper)
      *y = guppi_fn_wrapper_eval_d__d_d (cfi->y_binary_fn_wrapper, xx, yy);
    else if (cfi->y_fn_wrapper)
      *y = guppi_fn_wrapper_eval_d__d (cfi->y_fn_wrapper, yy);
    else /* default is identity fn. */
      *y = yy;
  }
}

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

#define add_arg(str, t, symb) \
gtk_object_add_arg_type("GuppiCurveFunc::" str, t, GTK_ARG_WRITABLE, symb)

static void
guppi_curve_func_class_init (GuppiCurveFuncClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *) klass;
  GuppiCurveClass *curve_class = GUPPI_CURVE_CLASS (klass);
  GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_CURVE);

  object_class->get_arg = guppi_curve_func_get_arg;
  object_class->set_arg = guppi_curve_func_set_arg;
  object_class->finalize = guppi_curve_func_finalize;

  data_class->is_leaf_type = TRUE;

  curve_class->bounds = v_curve_bounds;
  curve_class->get = v_curve_get;

  add_arg ("curve", GTK_TYPE_POINTER, ARG_CURVE);
  add_arg ("reparameterize", GTK_TYPE_BOOL, ARG_REPARAMETERIZE);
  add_arg ("t0", GTK_TYPE_DOUBLE, ARG_T0);
  add_arg ("t1", GTK_TYPE_DOUBLE, ARG_T1);
  add_arg ("t_function", GTK_TYPE_POINTER, ARG_T_FUNC);
  add_arg ("x_function", GTK_TYPE_POINTER, ARG_X_FUNC);
  add_arg ("y_function", GTK_TYPE_POINTER, ARG_Y_FUNC);
  add_arg ("x_binary_function", GTK_TYPE_POINTER, ARG_X_BINARY_FUNC);
  add_arg ("y_binary_function", GTK_TYPE_POINTER, ARG_Y_BINARY_FUNC);
  add_arg ("use_2x2_matrix", GTK_TYPE_BOOL, ARG_USE_2x2_MATRIX);
  add_arg ("matrix_a", GTK_TYPE_DOUBLE, ARG_MATRIX_A);
  add_arg ("matrix_b", GTK_TYPE_DOUBLE, ARG_MATRIX_B);
  add_arg ("matrix_c", GTK_TYPE_DOUBLE, ARG_MATRIX_C);
  add_arg ("matrix_d", GTK_TYPE_DOUBLE, ARG_MATRIX_D);

}

static void
guppi_curve_func_init (GuppiCurveFunc *obj)
{
  obj->t0 = 0;
  obj->t1 = 1;
}

GtkType guppi_curve_func_get_type (void)
{
  static GtkType guppi_curve_func_type = 0;
  if (!guppi_curve_func_type) {
    static const GtkTypeInfo guppi_curve_func_info = {
      "GuppiCurveFunc",
      sizeof (GuppiCurveFunc),
      sizeof (GuppiCurveFuncClass),
      (GtkClassInitFunc) guppi_curve_func_class_init,
      (GtkObjectInitFunc) guppi_curve_func_init,
      NULL, NULL, (GtkClassInitFunc) NULL
    };
    guppi_curve_func_type =
      gtk_type_unique (GUPPI_TYPE_CURVE, &guppi_curve_func_info);
  }
  return guppi_curve_func_type;
}

/**************************************************************************/
#if 0
GuppiData *
guppi_curve_new_reparameterize_linearly (double t0, double t1,
					 GuppiCurve *curve)
{
  g_return_val_if_fail (curve && GUPPI_IS_CURVE (curve), NULL);

  return guppi_data_new_by_type (GUPPI_TYPE_CURVE,
				 GUPPI_TYPE_CURVE_FUNC,
				 "curve", curve,
				 "t0", t0,
				 "t1", t1,
				 NULL);
}

GuppiData *
guppi_curve_new_reparameterize (double t0, double t1,
				GuppiFnWrapper *t_fn,
				GuppiCurve *curve)
{
  g_return_val_if_fail (curve && GUPPI_IS_CURVE (curve), NULL);
  g_return_val_if_fail (t_fn && GUPPI_IS_FN_WRAPPER (t_fn), NULL);

  return guppi_data_new_by_type (GUPPI_TYPE_CURVE,
				 GUPPI_TYPE_CURVE_FUNC,
				 "curve", curve,
				 "t0", t0,
				 "t1", t1,
				 "t_function", t_fn,
				 NULL);
}
#endif

/* $Id: guppi-curve-func.c,v 1.1 2002/01/08 06:28:59 trow Exp $ */


syntax highlighted by Code2HTML, v. 0.9.1