/* This is -*- C -*- */
/* $Id: guppi-curve.c,v 1.12 2001/08/21 02:29:57 trow Exp $ */
/*
* guppi-curve.c
*
* Copyright (C) 2000 EMC Capital Management, Inc.
*
* 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.h"
#include <gnan.h>
#include <guppi-convenient.h>
static GtkObjectClass *parent_class = NULL;
static void
guppi_curve_finalize (GtkObject *obj)
{
if (parent_class->finalize)
parent_class->finalize (obj);
}
static void
guppi_curve_class_init (GuppiCurveClass *klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
parent_class = gtk_type_class (GUPPI_TYPE_DATA);
object_class->finalize = guppi_curve_finalize;
}
static void
guppi_curve_init (GuppiCurve *obj)
{
}
GtkType guppi_curve_get_type (void)
{
static GtkType guppi_curve_type = 0;
if (!guppi_curve_type) {
static const GtkTypeInfo guppi_curve_info = {
"GuppiCurve",
sizeof (GuppiCurve),
sizeof (GuppiCurveClass),
(GtkClassInitFunc) guppi_curve_class_init,
(GtkObjectInitFunc) guppi_curve_init,
NULL, NULL, (GtkClassInitFunc) NULL
};
guppi_curve_type = gtk_type_unique (GUPPI_TYPE_DATA, &guppi_curve_info);
}
return guppi_curve_type;
}
void
guppi_curve_parameter_bounds (GuppiCurve *curve, double *a, double *b)
{
GuppiCurveClass *klass;
g_return_if_fail (GUPPI_IS_CURVE (curve));
if (a == NULL && b == NULL)
return;
klass = GUPPI_CURVE_CLASS (GTK_OBJECT (curve)->klass);
if (klass->bounds) {
klass->bounds (curve, a, b);
} else {
if (a) *a = -G_INFINITY;
if (b) *b = G_INFINITY;
}
}
double
guppi_curve_parameter_lower_bound (GuppiCurve *curve)
{
double t;
guppi_curve_parameter_bounds (curve, &t, NULL);
return t;
}
double
guppi_curve_parameter_upper_bound (GuppiCurve *curve)
{
double t;
guppi_curve_parameter_bounds (curve, NULL, &t);
return t;
}
gboolean
guppi_curve_parameter_in_bounds (GuppiCurve *curve, double t)
{
double a, b;
g_return_val_if_fail (GUPPI_IS_CURVE (curve), FALSE);
guppi_curve_parameter_bounds (curve, &a, &b);
return a <= t && t <= b;
}
double
guppi_curve_clamp_parameter (GuppiCurve *curve, double t)
{
double a, b;
g_return_val_if_fail (GUPPI_IS_CURVE (curve), FALSE);
guppi_curve_parameter_bounds (curve, &a, &b);
return CLAMP (t, a, b);
}
void
guppi_curve_get (GuppiCurve *curve, double t, double *x, double *y)
{
GuppiCurveClass *klass;
double a, b;
g_return_if_fail (GUPPI_IS_CURVE (curve));
if (x == NULL && y == NULL)
return;
klass = GUPPI_CURVE_CLASS (GTK_OBJECT (curve)->klass);
if (klass->bounds) {
klass->bounds (curve, &a, &b);
t = CLAMP (t, a, b);
}
g_assert (klass->get);
klass->get (curve, t, x, y);
}
double
guppi_curve_get_x (GuppiCurve *curve, double t)
{
double x;
g_return_val_if_fail (GUPPI_IS_CURVE (curve), 0);
guppi_curve_get (curve, t, &x, NULL);
return x;
}
double
guppi_curve_get_y (GuppiCurve *curve, double t)
{
double y;
g_return_val_if_fail (curve != NULL, 0);
g_return_val_if_fail (GUPPI_IS_CURVE (curve), 0);
guppi_curve_get (curve, t, NULL, &y);
return y;
}
void
guppi_curve_get_bbox (GuppiCurve *curve, double t0, double t1,
double *x0, double *y0, double *x1, double *y1)
{
GuppiCurveClass *klass;
double a, b;
g_return_if_fail (GUPPI_IS_CURVE (curve));
klass = GUPPI_CURVE_CLASS (GTK_OBJECT (curve)->klass);
guppi_2sort (&t0, &t1);
if (klass->bounds) {
klass->bounds (curve, &a, &b);
t0 = CLAMP (t0, a, b);
t1 = CLAMP (t1, a, b);
}
if (klass->bbox) {
klass->bbox (curve, t0, t1, x0, y0, x1, y1);
} else {
double x, y, bbx0 = 0, bbx1 = 0, bby0 = 0, bby1 = 0;
const gint N = 200;
gint i;
g_assert (klass->get);
/* An extremely ineffecient and hacky approximation */
for (i = 0; i <= N; ++i) {
double t = t0 + i * (t1 - t0) / N;
klass->get (curve, t, &x, &y);
if (i == 0) {
bbx0 = bbx1 = x;
bby0 = bby1 = y;
} else {
if (x < bbx0)
bbx0 = x;
if (x > bbx1)
bbx1 = x;
if (y < bby0)
bby0 = y;
if (y > bby1)
bby1 = y;
}
}
if (x0)
*x0 = bbx0;
if (x1)
*x1 = bbx1;
if (y0)
*y0 = bby0;
if (y1)
*y1 = bby1;
}
}
void
guppi_curve_clamp_to_bbox (GuppiCurve *curve,
double *t0, double *t1,
double x0, double y0, double x1, double y1)
{
GuppiCurveClass *klass;
double a, b, bt0=0, bt1=1, small_step;
double x, y;
gint i, total_steps;
gboolean overall_done;
g_return_if_fail (GUPPI_IS_CURVE (curve));
klass = GUPPI_CURVE_CLASS (GTK_OBJECT (curve)->klass);
guppi_2sort (&x0, &x1);
guppi_2sort (&y0, &y1);
guppi_curve_parameter_bounds (curve, &a, &b);
if (t0) bt0 = *t0;
if (t1) bt1 = *t1;
if (! (klass->clamp && klass->clamp (curve, t0, t1, x0, y0, x1, y1))) {
if (t0) *t0 = bt0;
if (t1) *t1 = bt1;
bt0 = a;
bt1 = b;
overall_done = FALSE;
for (i = 0; i < 3 && !overall_done; ++i) {
small_step = (bt1 - bt0)/20;
total_steps = 0;
if (t0) {
gboolean done = FALSE, stepped = FALSE;
while (!done && *t0 < bt1) {
guppi_curve_get (curve, *t0, &x, &y);
if (! (guppi_between (x0, x, x1) && guppi_between (y0, y, y1))) {
*t0 += small_step;
stepped = TRUE;
++total_steps;
} else
done = TRUE;
}
if (stepped) {
*t0 -= small_step;
--total_steps;
}
}
if (t1) {
gboolean done = FALSE, stepped = FALSE;
while (!done && *t1 > bt0) {
guppi_curve_get (curve, *t1, &x, &y);
if (! (guppi_between (x0, x, x1) && guppi_between (y0, y, y1))) {
*t1 -= small_step;
stepped = TRUE;
++total_steps;
} else
done = TRUE;
}
if (stepped) {
*t1 += small_step;
--total_steps;
}
}
if (t0 && t1 && total_steps > 0) {
bt0 = *t0;
bt1 = *t1;
} else
overall_done = TRUE;
}
}
if (t0)
*t0 = CLAMP (*t0, a, b);
if (t1)
*t1 = CLAMP (*t1, a, b);
}
void
guppi_curve_sample (GuppiCurve *curve,
const double *t_vec, gint t_stride, gsize N,
double *x_vec, gint x_stride,
double *y_vec, gint y_stride)
{
GuppiCurveClass *klass;
g_return_if_fail (GUPPI_IS_CURVE (curve));
g_return_if_fail (t_vec != NULL);
if (N == 0 || (x_vec == NULL && y_vec == NULL))
return;
klass = GUPPI_CURVE_CLASS (GTK_OBJECT (curve)->klass);
if (klass->sample) {
klass->sample (curve, t_vec, t_stride, N,
x_vec, x_stride, y_vec, y_stride);
} else {
const guint8 *t_ptr = (const guint8 *) t_vec;
guint8 *x_ptr = (guint8 *) x_vec;
guint8 *y_ptr = (guint8 *) y_vec;
double a, b, t;
guppi_curve_parameter_bounds (curve, &a, &b);
g_assert (klass->get);
while (N) {
t = *(const double *) t_ptr;
t = CLAMP (t, a, b);
klass->get (curve, t, (double *) x_ptr, (double *) y_ptr);
t_ptr += t_stride;
if (x_ptr)
x_ptr += x_stride;
if (y_ptr)
y_ptr += y_stride;
--N;
}
}
}
void
guppi_curve_sample_uniformly (GuppiCurve *curve,
double t0, double t1, gsize N,
double *x_vec, gint x_stride,
double *y_vec, gint y_stride)
{
GuppiCurveClass *klass;
double a, b;
g_return_if_fail (GUPPI_IS_CURVE (curve));
if (N == 0 || (x_vec == NULL && y_vec == NULL))
return;
guppi_curve_parameter_bounds (curve, &a, &b);
t0 = CLAMP (t0, a, b);
t1 = CLAMP (t1, a, b);
klass = GUPPI_CURVE_CLASS (GTK_OBJECT (curve)->klass);
if (N == 1) {
klass->get (curve, (t0 + t1) / 2, x_vec, y_vec);
return;
}
if (klass->sample_uniformly) {
klass->sample_uniformly (curve, t0, t1, N,
x_vec, x_stride, y_vec, y_stride);
} else {
double *t_vec = guppi_new (double, N);
gsize i;
for (i = 0; i < N; ++i)
t_vec[i] = t0 + i * (t1 - t0) / (N - 1);
guppi_curve_sample (curve, t_vec, sizeof (double), N,
x_vec, x_stride, y_vec, y_stride);
guppi_free (t_vec);
}
}
void
guppi_curve_sample_uniformly_to_path (GuppiCurve *curve,
double t0, double t1, gsize N,
ArtVpath *path)
{
gint i;
g_return_if_fail (curve != NULL);
g_return_if_fail (GUPPI_IS_CURVE (curve));
g_return_if_fail (path != NULL);
if (N == 0)
return;
guppi_curve_sample_uniformly (curve, t0, t1, N,
&path[0].x, sizeof (ArtVpath),
&path[0].y, sizeof (ArtVpath));
path[0].code = ART_MOVETO_OPEN;
for (i = 1; i < N; ++i)
path[i].code = ART_LINETO;
path[N].code = ART_END;
}
ArtVpath *
guppi_curve_approximate_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)
{
GuppiCurveClass *klass;
ArtVpath *path;
g_return_val_if_fail (GUPPI_IS_CURVE (curve), NULL);
g_return_val_if_fail (x_error > 0 && y_error > 0, NULL);
guppi_2sort (&t0, &t1);
klass = GUPPI_CURVE_CLASS (GTK_OBJECT (curve)->klass);
if (klass->approx_to_path
&& (path = klass->approx_to_path (curve, t0, t1, x_error, y_error,
x0, y0, x1, y1,
scale_x, scale_y)))
return path;
/* A stupid hack, for now. */
path = guppi_new0 (ArtVpath, 21);
guppi_curve_sample_uniformly_to_path (curve, t0, t1, 20, path);
return path;
}
/* $Id: guppi-curve.c,v 1.12 2001/08/21 02:29:57 trow Exp $ */
syntax highlighted by Code2HTML, v. 0.9.1