medit/moo/libfoocanvas/foo-canvas-polygon.c
2007-05-26 02:43:55 -05:00

822 lines
22 KiB
C

/*
* Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
* All rights reserved.
*
* This file is part of the Gnome Library.
*
* The Gnome Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The Gnome Library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Gnome Library; see the file COPYING.LIB. If not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
@NOTATION@
*/
/* Polygon item type for FooCanvas widget
*
* FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
* copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
*
* Author: Federico Mena <federico@nuclecu.unam.mx>
*/
#include <config.h>
#include <math.h>
#include <string.h>
#include "libfoocanvas.h"
#define NUM_STATIC_POINTS 256 /* Number of static points to use to avoid allocating arrays */
#define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) { \
if (x < bx1) \
bx1 = x; \
\
if (x > bx2) \
bx2 = x; \
\
if (y < by1) \
by1 = y; \
\
if (y > by2) \
by2 = y; \
}
enum {
PROP_0,
PROP_POINTS,
PROP_FILL_COLOR,
PROP_FILL_COLOR_GDK,
PROP_FILL_COLOR_RGBA,
PROP_OUTLINE_COLOR,
PROP_OUTLINE_COLOR_GDK,
PROP_OUTLINE_COLOR_RGBA,
PROP_FILL_STIPPLE,
PROP_OUTLINE_STIPPLE,
PROP_WIDTH_PIXELS,
PROP_WIDTH_UNITS
};
static void foo_canvas_polygon_class_init (FooCanvasPolygonClass *class);
static void foo_canvas_polygon_init (FooCanvasPolygon *poly);
static void foo_canvas_polygon_destroy (GtkObject *object);
static void foo_canvas_polygon_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void foo_canvas_polygon_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void foo_canvas_polygon_update (FooCanvasItem *item,
double i2w_dx, double i2w_dy,
FooCanvasUpdateFlags flags);
static void foo_canvas_polygon_realize (FooCanvasItem *item);
static void foo_canvas_polygon_unrealize (FooCanvasItem *item);
static void foo_canvas_polygon_draw (FooCanvasItem *item, GdkDrawable *drawable,
GdkEventExpose *expose);
static double foo_canvas_polygon_point (FooCanvasItem *item, double x, double y,
int cx, int cy, FooCanvasItem **actual_item);
static void foo_canvas_polygon_translate (FooCanvasItem *item, double dx, double dy);
static void foo_canvas_polygon_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
G_DEFINE_TYPE(FooCanvasPolygon, foo_canvas_polygon, FOO_TYPE_CANVAS_ITEM)
static void
foo_canvas_polygon_class_init (FooCanvasPolygonClass *class)
{
GObjectClass *gobject_class;
GtkObjectClass *object_class;
FooCanvasItemClass *item_class;
gobject_class = (GObjectClass *) class;
object_class = (GtkObjectClass *) class;
item_class = (FooCanvasItemClass *) class;
gobject_class->set_property = foo_canvas_polygon_set_property;
gobject_class->get_property = foo_canvas_polygon_get_property;
g_object_class_install_property
(gobject_class,
PROP_POINTS,
g_param_spec_boxed ("points", NULL, NULL,
FOO_TYPE_CANVAS_POINTS,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_FILL_COLOR,
g_param_spec_string ("fill-color", NULL, NULL,
NULL,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_FILL_COLOR_GDK,
g_param_spec_boxed ("fill-color-gdk", NULL, NULL,
GDK_TYPE_COLOR,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_FILL_COLOR_RGBA,
g_param_spec_uint ("fill-color-rgba", NULL, NULL,
0, G_MAXUINT, 0,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_OUTLINE_COLOR,
g_param_spec_string ("outline-color", NULL, NULL,
NULL,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_OUTLINE_COLOR_GDK,
g_param_spec_boxed ("outline-color-gdk", NULL, NULL,
GDK_TYPE_COLOR,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_OUTLINE_COLOR_RGBA,
g_param_spec_uint ("outline-color-rgba", NULL, NULL,
0, G_MAXUINT, 0,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_FILL_STIPPLE,
g_param_spec_object ("fill-stipple", NULL, NULL,
GDK_TYPE_DRAWABLE,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_OUTLINE_STIPPLE,
g_param_spec_object ("outline-stipple", NULL, NULL,
GDK_TYPE_DRAWABLE,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_WIDTH_PIXELS,
g_param_spec_uint ("width-pixels", NULL, NULL,
0, G_MAXUINT, 0,
G_PARAM_READWRITE));
g_object_class_install_property
(gobject_class,
PROP_WIDTH_UNITS,
g_param_spec_double ("width-units", NULL, NULL,
0.0, G_MAXDOUBLE, 0.0,
G_PARAM_READWRITE));
object_class->destroy = foo_canvas_polygon_destroy;
item_class->update = foo_canvas_polygon_update;
item_class->realize = foo_canvas_polygon_realize;
item_class->unrealize = foo_canvas_polygon_unrealize;
item_class->draw = foo_canvas_polygon_draw;
item_class->point = foo_canvas_polygon_point;
item_class->translate = foo_canvas_polygon_translate;
item_class->bounds = foo_canvas_polygon_bounds;
}
static void
foo_canvas_polygon_init (FooCanvasPolygon *poly)
{
poly->width = 0.0;
}
static void
foo_canvas_polygon_destroy (GtkObject *object)
{
FooCanvasPolygon *poly;
g_return_if_fail (object != NULL);
g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
poly = FOO_CANVAS_POLYGON (object);
/* remember, destroy can be run multiple times! */
if (poly->coords)
g_free (poly->coords);
poly->coords = NULL;
if (poly->fill_stipple)
g_object_unref (poly->fill_stipple);
poly->fill_stipple = NULL;
if (poly->outline_stipple)
g_object_unref (poly->outline_stipple);
poly->outline_stipple = NULL;
if (GTK_OBJECT_CLASS (foo_canvas_polygon_parent_class)->destroy)
GTK_OBJECT_CLASS (foo_canvas_polygon_parent_class)->destroy (object);
}
/* Computes the bounding box of the polygon. Assumes that the number of points in the polygon is
* not zero.
*/
static gboolean
get_bounds (FooCanvasPolygon *poly, double *bx1, double *by1, double *bx2, double *by2)
{
double *coords;
double x1, y1, x2, y2;
double width;
int i;
if (poly->num_points == 0)
return FALSE;
/* Compute bounds of vertices */
x1 = x2 = poly->coords[0];
y1 = y2 = poly->coords[1];
for (i = 1, coords = poly->coords + 2; i < poly->num_points; i++, coords += 2) {
GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
}
/* Add outline width */
if (poly->width_pixels)
width = poly->width / poly->item.canvas->pixels_per_unit;
else
width = poly->width;
width /= 2.0;
x1 -= width;
y1 -= width;
x2 += width;
y2 += width;
/* Done */
*bx1 = x1;
*by1 = y1;
*bx2 = x2;
*by2 = y2;
return TRUE;
}
/* Computes the bounding box of the polygon, in canvas coordinates. Assumes that the number of points in the polygon is
* not zero.
*/
static gboolean
get_bounds_canvas (FooCanvasPolygon *poly,
double *bx1, double *by1, double *bx2, double *by2,
double i2w_dx, double i2w_dy)
{
FooCanvasItem *item;
double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
item = FOO_CANVAS_ITEM (poly);
if (!get_bounds (poly, &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1))
return FALSE;
bbox_x0 += i2w_dx;
bbox_y0 += i2w_dy;
bbox_x1 += i2w_dx;
bbox_y1 += i2w_dy;
foo_canvas_w2c_rect_d (item->canvas,
&bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1);
/* include 1 pixel of fudge */
*bx1 = bbox_x0 - 1;
*by1 = bbox_y0 - 1;
*bx2 = bbox_x1 + 1;
*by2 = bbox_y1 + 1;
return TRUE;
}
/* Sets the points of the polygon item to the specified ones. If needed, it will add a point to
* close the polygon.
*/
static void
set_points (FooCanvasPolygon *poly, FooCanvasPoints *points)
{
int duplicate;
/* See if we need to duplicate the first point */
duplicate = ((points->coords[0] != points->coords[2 * points->num_points - 2])
|| (points->coords[1] != points->coords[2 * points->num_points - 1]));
if (duplicate)
poly->num_points = points->num_points + 1;
else
poly->num_points = points->num_points;
poly->coords = g_new (double, 2 * poly->num_points);
memcpy (poly->coords, points->coords, 2 * points->num_points * sizeof (double));
if (duplicate) {
poly->coords[2 * poly->num_points - 2] = poly->coords[0];
poly->coords[2 * poly->num_points - 1] = poly->coords[1];
}
}
/* Convenience function to set a GC's foreground color to the specified pixel value */
static void
set_gc_foreground (GdkGC *gc, gulong pixel)
{
GdkColor c;
if (!gc)
return;
c.pixel = pixel;
gdk_gc_set_foreground (gc, &c);
}
/* Sets the stipple pattern for the specified gc */
static void
set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure)
{
if (*internal_stipple && !reconfigure)
g_object_unref (*internal_stipple);
*internal_stipple = stipple;
if (stipple && !reconfigure)
g_object_ref (stipple);
if (gc) {
if (stipple) {
gdk_gc_set_stipple (gc, stipple);
gdk_gc_set_fill (gc, GDK_STIPPLED);
} else
gdk_gc_set_fill (gc, GDK_SOLID);
}
}
/* Recalculate the outline width of the polygon and set it in its GC */
static void
set_outline_gc_width (FooCanvasPolygon *poly)
{
int width;
if (!poly->outline_gc)
return;
if (poly->width_pixels)
width = (int) poly->width;
else
width = (int) (poly->width * poly->item.canvas->pixels_per_unit + 0.5);
gdk_gc_set_line_attributes (poly->outline_gc, width,
GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
}
static void
foo_canvas_polygon_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
FooCanvasItem *item;
FooCanvasPolygon *poly;
FooCanvasPoints *points;
GdkColor color = { 0, 0, 0, 0, };
GdkColor *pcolor;
int have_pixel;
g_return_if_fail (object != NULL);
g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
item = FOO_CANVAS_ITEM (object);
poly = FOO_CANVAS_POLYGON (object);
have_pixel = FALSE;
switch (param_id) {
case PROP_POINTS:
points = g_value_get_boxed (value);
if (poly->coords) {
g_free (poly->coords);
poly->coords = NULL;
}
if (!points)
poly->num_points = 0;
else
set_points (poly, points);
foo_canvas_item_request_update (item);
break;
case PROP_FILL_COLOR:
case PROP_FILL_COLOR_GDK:
case PROP_FILL_COLOR_RGBA:
switch (param_id) {
case PROP_FILL_COLOR:
if (g_value_get_string (value) &&
gdk_color_parse (g_value_get_string (value), &color))
poly->fill_set = TRUE;
else
poly->fill_set = FALSE;
poly->fill_color = ((color.red & 0xff00) << 16 |
(color.green & 0xff00) << 8 |
(color.blue & 0xff00) |
0xff);
break;
case PROP_FILL_COLOR_GDK:
pcolor = g_value_get_boxed (value);
poly->fill_set = pcolor != NULL;
if (pcolor) {
GdkColormap *colormap;
color = *pcolor;
colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
gdk_rgb_find_color (colormap, &color);
have_pixel = TRUE;
}
poly->fill_color = ((color.red & 0xff00) << 16 |
(color.green & 0xff00) << 8 |
(color.blue & 0xff00) |
0xff);
break;
case PROP_FILL_COLOR_RGBA:
poly->fill_set = TRUE;
poly->fill_color = g_value_get_uint (value);
break;
}
#ifdef VERBOSE
g_print ("poly fill color = %08x\n", poly->fill_color);
#endif
if (have_pixel)
poly->fill_pixel = color.pixel;
else
poly->fill_pixel = foo_canvas_get_color_pixel (item->canvas,
poly->fill_color);
set_gc_foreground (poly->fill_gc, poly->fill_pixel);
foo_canvas_item_request_redraw (item);
break;
case PROP_OUTLINE_COLOR:
case PROP_OUTLINE_COLOR_GDK:
case PROP_OUTLINE_COLOR_RGBA:
switch (param_id) {
case PROP_OUTLINE_COLOR:
if (g_value_get_string (value) &&
gdk_color_parse (g_value_get_string (value), &color))
poly->outline_set = TRUE;
else
poly->outline_set = FALSE;
poly->outline_color = ((color.red & 0xff00) << 16 |
(color.green & 0xff00) << 8 |
(color.blue & 0xff00) |
0xff);
break;
case PROP_OUTLINE_COLOR_GDK:
pcolor = g_value_get_boxed (value);
poly->outline_set = pcolor != NULL;
if (pcolor) {
GdkColormap *colormap;
color = *pcolor;
colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
gdk_rgb_find_color (colormap, &color);
have_pixel = TRUE;
}
poly->outline_color = ((color.red & 0xff00) << 16 |
(color.green & 0xff00) << 8 |
(color.blue & 0xff00) |
0xff);
break;
case PROP_OUTLINE_COLOR_RGBA:
poly->outline_set = TRUE;
poly->outline_color = g_value_get_uint (value);
break;
}
#ifdef VERBOSE
g_print ("poly outline color = %08x\n", poly->outline_color);
#endif
if (have_pixel)
poly->outline_pixel = color.pixel;
else
poly->outline_pixel = foo_canvas_get_color_pixel (item->canvas,
poly->outline_color);
set_gc_foreground (poly->outline_gc, poly->outline_pixel);
foo_canvas_item_request_redraw (item);
break;
case PROP_FILL_STIPPLE:
set_stipple (poly->fill_gc, &poly->fill_stipple, (GdkBitmap *) g_value_get_object (value), FALSE);
foo_canvas_item_request_update (item);
break;
case PROP_OUTLINE_STIPPLE:
set_stipple (poly->outline_gc, &poly->outline_stipple, (GdkBitmap *) g_value_get_object (value), FALSE);
foo_canvas_item_request_update (item);
break;
case PROP_WIDTH_PIXELS:
poly->width = g_value_get_uint (value);
poly->width_pixels = TRUE;
set_outline_gc_width (poly);
#ifdef OLD_XFORM
recalc_bounds (poly);
#else
foo_canvas_item_request_update (item);
#endif
break;
case PROP_WIDTH_UNITS:
poly->width = fabs (g_value_get_double (value));
poly->width_pixels = FALSE;
set_outline_gc_width (poly);
#ifdef OLD_XFORM
recalc_bounds (poly);
#else
foo_canvas_item_request_update (item);
#endif
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
/* Allocates a GdkColor structure filled with the specified pixel, and puts it into the specified
* value for returning it in the get_property method.
*/
static void
get_color_value (FooCanvasPolygon *poly, gulong pixel, GValue *value)
{
GdkColor *color;
GdkColormap *colormap;
color = g_new (GdkColor, 1);
color->pixel = pixel;
colormap = gtk_widget_get_colormap (GTK_WIDGET (poly));
gdk_rgb_find_color (colormap, color);
g_value_set_boxed (value, color);
}
static void
foo_canvas_polygon_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
FooCanvasPolygon *poly;
FooCanvasPoints *points;
g_return_if_fail (object != NULL);
g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
poly = FOO_CANVAS_POLYGON (object);
switch (param_id) {
case PROP_POINTS:
if (poly->num_points != 0) {
points = foo_canvas_points_new (poly->num_points);
memcpy (points->coords, poly->coords, 2 * poly->num_points * sizeof (double));
g_value_set_boxed (value, points);
} else
g_value_set_boxed (value, NULL);
break;
case PROP_FILL_COLOR_GDK:
get_color_value (poly, poly->fill_pixel, value);
break;
case PROP_OUTLINE_COLOR_GDK:
get_color_value (poly, poly->outline_pixel, value);
break;
case PROP_FILL_COLOR_RGBA:
g_value_set_uint (value, poly->fill_color);
break;
case PROP_OUTLINE_COLOR_RGBA:
g_value_set_uint (value, poly->outline_color);
break;
case PROP_FILL_STIPPLE:
g_value_set_object (value, (GObject *) poly->fill_stipple);
break;
case PROP_OUTLINE_STIPPLE:
g_value_set_object (value, (GObject *) poly->outline_stipple);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
foo_canvas_polygon_update (FooCanvasItem *item,
double i2w_dx, double i2w_dy,
FooCanvasUpdateFlags flags)
{
FooCanvasPolygon *poly;
double x1, y1, x2, y2;
poly = FOO_CANVAS_POLYGON (item);
if (FOO_CANVAS_ITEM_CLASS(foo_canvas_polygon_parent_class)->update)
FOO_CANVAS_ITEM_CLASS(foo_canvas_polygon_parent_class)->update (item, i2w_dx, i2w_dy, flags);
set_outline_gc_width (poly);
set_gc_foreground (poly->fill_gc, poly->fill_pixel);
set_gc_foreground (poly->outline_gc, poly->outline_pixel);
set_stipple (poly->fill_gc, &poly->fill_stipple, poly->fill_stipple, TRUE);
set_stipple (poly->outline_gc, &poly->outline_stipple, poly->outline_stipple, TRUE);
if (get_bounds_canvas (poly, &x1, &y1, &x2, &y2, i2w_dx, i2w_dy))
foo_canvas_update_bbox (item, x1, y1, x2, y2);
}
static void
foo_canvas_polygon_realize (FooCanvasItem *item)
{
FooCanvasPolygon *poly;
poly = FOO_CANVAS_POLYGON (item);
if (FOO_CANVAS_ITEM_CLASS(foo_canvas_polygon_parent_class)->realize)
FOO_CANVAS_ITEM_CLASS(foo_canvas_polygon_parent_class)->realize (item);
poly->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
poly->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
#warning "FIXME: Need to recalc pixel values, set colours, etc."
#ifdef OLD_XFORM
(* FOO_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
#endif
}
static void
foo_canvas_polygon_unrealize (FooCanvasItem *item)
{
FooCanvasPolygon *poly;
poly = FOO_CANVAS_POLYGON (item);
g_object_unref (poly->fill_gc);
poly->fill_gc = NULL;
g_object_unref (poly->outline_gc);
poly->outline_gc = NULL;
if (FOO_CANVAS_ITEM_CLASS(foo_canvas_polygon_parent_class)->unrealize)
FOO_CANVAS_ITEM_CLASS(foo_canvas_polygon_parent_class)->unrealize (item);
}
/* Converts an array of world coordinates into an array of canvas pixel coordinates. Takes in the
* item->world deltas and the drawable deltas.
*/
static void
item_to_canvas (FooCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
double i2w_dx, double i2w_dy)
{
int i;
for (i = 0; i < num_points; i++) {
foo_canvas_w2c (canvas,
item_coords[i*2] + i2w_dx,
item_coords[i*2+1] + i2w_dy,
&canvas_coords->x, &canvas_coords->y);
canvas_coords++;
}
}
static void
foo_canvas_polygon_draw (FooCanvasItem *item, GdkDrawable *drawable,
GdkEventExpose *expose)
{
FooCanvasPolygon *poly;
GdkPoint static_points[NUM_STATIC_POINTS];
GdkPoint *points;
double i2w_dx, i2w_dy;
poly = FOO_CANVAS_POLYGON (item);
if (poly->num_points == 0)
return;
/* Build array of canvas pixel coordinates */
if (poly->num_points <= NUM_STATIC_POINTS)
points = static_points;
else
points = g_new (GdkPoint, poly->num_points);
i2w_dx = 0.0;
i2w_dy = 0.0;
foo_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
item_to_canvas (item->canvas,
poly->coords, points, poly->num_points,
i2w_dx, i2w_dy);
if (poly->fill_set) {
if (poly->fill_stipple)
foo_canvas_set_stipple_origin (item->canvas, poly->fill_gc);
gdk_draw_polygon (drawable, poly->fill_gc, TRUE, points, poly->num_points);
}
if (poly->outline_set) {
if (poly->outline_stipple)
foo_canvas_set_stipple_origin (item->canvas, poly->outline_gc);
gdk_draw_polygon (drawable, poly->outline_gc, FALSE, points, poly->num_points);
}
/* Done */
if (points != static_points)
g_free (points);
}
static double
foo_canvas_polygon_point (FooCanvasItem *item, double x, double y,
int cx, int cy, FooCanvasItem **actual_item)
{
FooCanvasPolygon *poly;
double dist;
double width;
poly = FOO_CANVAS_POLYGON (item);
*actual_item = item;
dist = foo_canvas_polygon_to_point (poly->coords, poly->num_points, x, y);
if (poly->outline_set) {
if (poly->width_pixels)
width = poly->width / item->canvas->pixels_per_unit;
else
width = poly->width;
dist -= width / 2.0;
if (dist < 0.0)
dist = 0.0;
}
return dist;
}
static void
foo_canvas_polygon_translate (FooCanvasItem *item, double dx, double dy)
{
FooCanvasPolygon *poly;
int i;
double *coords;
poly = FOO_CANVAS_POLYGON (item);
for (i = 0, coords = poly->coords; i < poly->num_points; i++, coords += 2) {
coords[0] += dx;
coords[1] += dy;
}
}
static void
foo_canvas_polygon_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
{
FooCanvasPolygon *poly;
g_return_if_fail (item != NULL);
g_return_if_fail (FOO_IS_CANVAS_POLYGON (item));
poly = FOO_CANVAS_POLYGON (item);
if (poly->num_points == 0) {
*x1 = *y1 = *x2 = *y2 = 0.0;
return;
}
get_bounds (poly, x1, y1, x2, y2);
}