/* 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 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-curve.h" #include #include 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 $ */