/* * Curve.c -- Implementation of curve item. * * Authors : Patrick Lecoanet. * Creation date : Fri Mar 25 15:32:17 1994 * * $Id$ */ /* * Copyright (c) 1993 - 1999 CENA, Patrick Lecoanet -- * * This code 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. * * This code 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 this code; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "Draw.h" #include "Item.h" #include "Geo.h" #include "Types.h" #include "WidgetInfo.h" #include "Image.h" #include "Color.h" #ifdef GPC #include "gpc/gpc.h" #endif #include #include static const char rcsid[] = "$Id$"; static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; /* * Bit offset of flags. */ #define FILLED_BIT 1<<0 /* If the item is filled with color/pattern */ #define MARKED_BIT 1<<1 /* If the vertices are marked by a symbol */ #define CLOSED_BIT 1<<2 /* If the outline should be closed automatically */ #define REDUCED_BIT 1<<5 /* Tell if the contours are in the most reduced * form. */ #define FIRST_END_OK 1<<6 #define LAST_END_OK 1<<7 #define FILLED_OK 1<<8 #define RELIEF_OK 1<<10 #define MARKER_OK 1<<12 /* ********************************************************************************** * * Specific Curve item record * ********************************************************************************** */ typedef struct _CurveItemStruct { ItemStruct header; /* Public data */ ZnPoly shape; unsigned int flags; Pixmap marker; ZnLineEnd first_end; /* These two are considered only if relief is flat */ ZnLineEnd last_end; LineStyle line_style; /* This is considered only if relief is flat */ int cap_style; int join_style; ReliefStyle relief; int line_width; /* If 0 the path is not drawn, if <2 relief is flat */ Pixmap fill_pattern; ZnColorGradient fill_color; ZnGradientGeom grad_geom; Pixmap line_pattern; ZnColor line_color; ZnColor marker_color; int line_alpha; int fill_alpha; char *tile_name; /* Private data */ ZnImage tile; ZnPoly dev_shape; ZnColorGradient gradient; ArtSVP *outline_svp; ArtSVP *fill_svp; #ifdef GPC gpc_tristrip tristrip; #endif } CurveItemStruct, *CurveItem; static ZnAttrConfig cv_attrs[] = { { ZN_CONFIG_CAP_STYLE, "-capstyle", NULL, Tk_Offset(CurveItemStruct, cap_style), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-closed", NULL, Tk_Offset(CurveItemStruct, flags), CLOSED_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-composerotation", NULL, Tk_Offset(CurveItemStruct, header.flags), COMPOSE_ROTATION_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-composescale", NULL, Tk_Offset(CurveItemStruct, header.flags), COMPOSE_SCALE_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_UINT, "-fillalpha", NULL, Tk_Offset(CurveItemStruct, fill_alpha), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_GRADIENT_COLOR, "-fillcolor", NULL, Tk_Offset(CurveItemStruct, fill_color), 0, ZN_DRAW_FLAG|ZN_BORDER_FLAG, False }, { ZN_CONFIG_PATTERN, "-fillpattern", NULL, Tk_Offset(CurveItemStruct, fill_pattern), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-filled", NULL, Tk_Offset(CurveItemStruct, flags), FILLED_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_LINE_END, "-firstend", NULL, Tk_Offset(CurveItemStruct, first_end), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT_GEOM, "-gradient", NULL, Tk_Offset(CurveItemStruct, grad_geom), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_JOIN_STYLE, "-joinstyle", NULL, Tk_Offset(CurveItemStruct, join_style), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_LINE_END, "-lastend", NULL, Tk_Offset(CurveItemStruct, last_end), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_UINT, "-linealpha", NULL, Tk_Offset(CurveItemStruct, line_alpha), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_COLOR, "-linecolor", NULL, Tk_Offset(CurveItemStruct, line_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_PATTERN, "-linepattern", NULL, Tk_Offset(CurveItemStruct, line_pattern), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_LINE_STYLE, "-linestyle", NULL, Tk_Offset(CurveItemStruct, line_style), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_DIM, "-linewidth", NULL, Tk_Offset(CurveItemStruct, line_width), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_PRI, "-priority", NULL, Tk_Offset(CurveItemStruct, header.priority), 0, ZN_DRAW_FLAG|ZN_REPICK_FLAG, False }, { ZN_CONFIG_PATTERN, "-marker", NULL, Tk_Offset(CurveItemStruct, marker), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_COLOR, "-markercolor", NULL, Tk_Offset(CurveItemStruct, marker_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_RELIEF, "-relief", NULL, Tk_Offset(CurveItemStruct, relief), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(CurveItemStruct, header.flags), SENSITIVE_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_TAGS, "-tags", NULL, Tk_Offset(CurveItemStruct, header.tags), 0, 0, False }, { ZN_CONFIG_IMAGE, "-tile", NULL, Tk_Offset(CurveItemStruct, tile_name), 0, ZN_DRAW_FLAG|ZN_TILE_FLAG, False }, { ZN_CONFIG_BOOL, "-visible", NULL, Tk_Offset(CurveItemStruct, header.flags), VISIBLE_BIT, ZN_DRAW_FLAG|ZN_REPICK_FLAG|ZN_VIS_FLAG, False }, { ZN_CONFIG_END, NULL, NULL, 0, 0, 0 } }; /* ********************************************************************************** * * CvTileChange -- * ********************************************************************************** */ static void CvTileChange(ClientData client_data, int x, int y, int width, int height, int image_width, int image_height) { CurveItem cv = (CurveItem) client_data; InvalidateImage(cv->tile_name); ITEM.Invalidate((Item) cv, ZN_COORDS_FLAG); } /* ********************************************************************************** * * Init -- * ********************************************************************************** */ static int Init(Item item, int *argc, Tcl_Obj *CONST *args[]) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; Tcl_Obj **elems; int i, num_elems; ZnPoint *p, *points; double dbl; POLY_INIT(&cv->dev_shape); #ifdef GPC cv->tristrip.num_strips = 0; #endif cv->gradient = NULL; /* Init attributes */ SET(item->flags, VISIBLE_BIT); SET(item->flags, SENSITIVE_BIT); SET(item->flags, COMPOSE_ROTATION_BIT); SET(item->flags, COMPOSE_SCALE_BIT); SET(item->flags, CLOSED_BIT); SET(item->flags, REDUCED_BIT); item->priority = DEFAULT_CURVE_PRIORITY; if (*argc < 1) { Tcl_AppendResult(wi->interp, " curve coords expected", NULL); return ZN_ERROR; } if ((Tcl_ListObjGetElements(wi->interp, (*args)[0], &num_elems, &elems) == ZN_ERROR) || ((num_elems % 2) != 0)) { cv_error: Tcl_AppendResult(wi->interp, " malformed curve coords", NULL); return ZN_ERROR; } if (num_elems == 0) { POLY_INIT(&cv->shape); } else { p = points = (ZnPoint *) ZnMalloc(num_elems/2 * sizeof(ZnPoint)); POLY_CONTOUR1(&cv->shape, points, num_elems/2); for (i = 0; i < num_elems; i += 2, p++) { if (Tcl_GetDoubleFromObj(wi->interp, elems[i], &dbl) == ZN_ERROR) { cv_error2: POLY_FREE(&cv->shape); goto cv_error; } p->x = dbl; if (Tcl_GetDoubleFromObj(wi->interp, elems[i+1], &dbl) == ZN_ERROR) { goto cv_error2; } p->y = dbl; } } (*args)++; (*argc)--; CLEAR(cv->flags, FILLED_BIT); cv->first_end = NULL; cv->last_end = NULL; cv->line_style = LINE_SIMPLE; cv->relief = RELIEF_FLAT; cv->line_width = 1; cv->tile_name = ""; cv->tile = ZnUnspecifiedImage; cv->fill_pattern = ZnUnspecifiedPattern; cv->line_pattern = ZnUnspecifiedPattern; cv->cap_style = CapRound; cv->join_style = JoinRound; /* * In Tk marker visibility is controlled by the bitmap * being unspecified. */ SET(cv->flags, MARKED_BIT); cv->marker = ZnUnspecifiedPattern; cv->fill_color = ZnGetColorGradient(wi->interp, wi->win, ZnNameOfColor(wi->fore_color)); cv->line_color = ZnGetColorByValue(wi->win, wi->fore_color); cv->marker_color = ZnGetColorByValue(wi->win, wi->fore_color); cv->grad_geom = NULL; cv->outline_svp = cv->fill_svp = NULL; return ZN_OK; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(Item item) { CurveItem cv = (CurveItem) item; WidgetInfo *wi = item->wi; char *text; int i; ZnContour *conts; ZnBool *holes; POLY_INIT(&cv->dev_shape); if (cv->shape.num_contours) { conts = cv->shape.contours; holes = cv->shape.holes; if (cv->shape.contours != &cv->shape.contour1) { cv->shape.contours = (ZnContour *) ZnMalloc(cv->shape.num_contours*sizeof(ZnContour)); if (cv->shape.holes) { cv->shape.holes = (ZnBool *) ZnMalloc(cv->shape.num_contours*sizeof(ZnBool)); } } for (i = 0; i < cv->shape.num_contours; i++) { if (cv->shape.holes) { cv->shape.holes[i] = holes[i]; } cv->shape.contours[i].num_points = conts[i].num_points; cv->shape.contours[i].points = (ZnPoint *) ZnMalloc(conts[i].num_points*sizeof(ZnPoint)); memcpy(cv->shape.contours[i].points, conts[i].points, conts[i].num_points*sizeof(ZnPoint)); } } if (cv->gradient) { cv->gradient = ZnGetColorGradientByValue(cv->gradient); } if (cv->line_pattern != ZnUnspecifiedPattern) { cv->line_pattern = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, cv->line_pattern)); } if (cv->first_end) { LineEndDuplicate(cv->first_end); } if (cv->last_end) { LineEndDuplicate(cv->last_end); } if (strlen(cv->tile_name) != 0) { text = ZnMalloc((strlen(cv->tile_name) + 1) * sizeof(char)); strcpy(text, cv->tile_name); cv->tile_name = text; cv->tile = Tk_GetImage(wi->interp, wi->win, cv->tile_name, CvTileChange, (ClientData) cv); } if (cv->fill_pattern != ZnUnspecifiedPattern) { cv->fill_pattern = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, cv->fill_pattern)); } if (cv->marker != ZnUnspecifiedPattern) { cv->marker = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, cv->marker)); } cv->line_color = ZnGetColorByValue(wi->win, cv->line_color); cv->fill_color = ZnGetColorGradientByValue(cv->fill_color); cv->marker_color = ZnGetColorByValue(wi->win, cv->marker_color); if (cv->grad_geom) { cv->grad_geom = GradientGeomDuplicate(cv->grad_geom); } if (cv->outline_svp) { } if (cv->fill_svp) { } } /* ********************************************************************************** * * Destroy -- * ********************************************************************************** */ static void Destroy(Item item) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; POLY_FREE(&cv->shape); POLY_FREE(&cv->dev_shape); if (cv->first_end) { LineEndDelete(cv->first_end); } if (cv->last_end) { LineEndDelete(cv->last_end); } if (cv->gradient) { ZnFreeColorGradient(cv->gradient); } if (cv->tile != ZnUnspecifiedImage) { Tk_FreeImage(cv->tile); cv->tile = ZnUnspecifiedImage; } if (strlen(cv->tile_name) != 0) { ZnFree(cv->tile_name); } if (cv->line_pattern != ZnUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, cv->line_pattern); } if (cv->fill_pattern != ZnUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, cv->fill_pattern); } if (cv->marker != ZnUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, cv->marker); } ZnFreeColorGradient(cv->fill_color); ZnFreeColor(cv->line_color); ZnFreeColor(cv->marker_color); if (cv->grad_geom) { GradientGeomDelete(cv->grad_geom); } #ifdef GPC if (cv->tristrip.num_strips) { gpc_free_tristrip(&cv->tristrip); } #endif if (cv->outline_svp) { art_svp_free(cv->outline_svp); } if (cv->fill_svp) { art_svp_free(cv->fill_svp); } } /* ********************************************************************************** * * Setup flags to control the precedence between the * graphical attributes. * ********************************************************************************** */ static void SetRenderFlags(CurveItem cv) { ASSIGN(cv->flags, FILLED_OK, ISSET(cv->flags, FILLED_BIT) && (cv->shape.num_contours >= 1)); ASSIGN(cv->flags, RELIEF_OK, (cv->relief != RELIEF_FLAT) && (cv->shape.num_contours >= 1) && (cv->line_width > 1)); ASSIGN(cv->flags, MARKER_OK, (cv->marker != ZnUnspecifiedPattern) && ISCLEAR(cv->flags, RELIEF_OK)); ASSIGN(cv->flags, FIRST_END_OK, (cv->first_end != NULL) && (cv->shape.num_contours == 1) && (cv->shape.contours[0].num_points > 1) && ISCLEAR(cv->flags, FILLED_BIT) && cv->line_width && ISCLEAR(cv->flags, RELIEF_OK) && ISCLEAR(cv->flags, CLOSED_BIT)); ASSIGN(cv->flags, LAST_END_OK, (cv->last_end != NULL) && (cv->shape.num_contours == 1) && (cv->shape.contours[0].num_points > 1) && ISCLEAR(cv->flags, FILLED_BIT) && cv->line_width && ISCLEAR(cv->flags, RELIEF_OK) && ISCLEAR(cv->flags, CLOSED_BIT)); } /* ********************************************************************************** * * Configure -- * ********************************************************************************** */ static int Configure(Item item, int argc, Tcl_Obj *CONST argv[], int *flags) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; int status = ZN_OK; status = ITEM_P.ConfigureAttributes((char *) item, -1, argc, argv, flags); if (cv->gradient && (ISSET(*flags, ZN_BORDER_FLAG) || (cv->relief == RELIEF_FLAT))) { ZnFreeColorGradient(cv->gradient); cv->gradient = NULL; } if ((cv->relief != RELIEF_FLAT) && !cv->gradient) { cv->gradient = ZnGetReliefGradient(wi->interp, wi->win, ZnNameOfColor(ZnColorGradientMidColor(wi->win, cv->fill_color))); } if (ISSET(*flags, ZN_TILE_FLAG)) { Tk_Image tile; if (strcmp(cv->tile_name, "") != 0) { tile = Tk_GetImage(wi->interp, wi->win, cv->tile_name, CvTileChange, (ClientData) cv); if (tile == NULL) { /* * The name will not be in sync with the image in * this case. */ status = ZN_ERROR; } } else { tile = ZnUnspecifiedImage; } if (cv->tile != ZnUnspecifiedImage) { Tk_FreeImage(cv->tile); } cv->tile = tile; } return status; } /* ********************************************************************************** * * Query -- * ********************************************************************************** */ static int Query(Item item, int argc, Tcl_Obj *CONST argv[]) { if (ITEM_P.QueryAttribute((char *) item, -1, argv[0]) == ZN_ERROR) { return ZN_ERROR; } return ZN_OK; } static ZnBool TestCCW(ZnPoint *points, int num_points) { ZnPoint *p, *p_p, *p_n, min; ZnReal xprod; int i, min_index; if (num_points < 3) { return True; } /* * First find the lowest rightmost vertex. In X11 this is the * topmost one. */ p = points; min = *p; min_index = 0; for (i = 1, p++; i < num_points; i++, p++) { if ((p->y < min.y) || ((p->y == min.y) && (p->x > min.x))) { min_index = i; min = *p; } } /* * Then find the indices of the previous and next * vertices. */ p = &points[min_index]; /*printf("min index %d, prev %d, next %d\n", min_index, (min_index+(num_points-1))%num_points, (min_index+1)%num_points);*/ p_p = &points[(min_index+(num_points-1))%num_points]; /* min_index-1 */ p_n = &points[(min_index+1)%num_points]; xprod = ((p_p->x*p->y - p_p->y*p->x) + (p_p->y*p_n->x - p_p->x*p_n->y) + (p->x*p_n->y - p->y*p_n->x)); return (xprod <= 0.0); /* Should be >= 0 but X11 has Y axis reverted. */ } /* * Create a reduced polygon from an unknown one by * adding/clipping all the shapes/holes in turn. */ #ifdef GPC static void ReduceContours(ZnPoly *poly_in, ZnPoly *poly_out) { int i; ZnContour *c; ZnBool *hole; ZnPoly cpoly, rpoly; POLY_INIT(poly_out); for (i = 0, hole = poly_in->holes, c = poly_in->contours; i < poly_in->num_contours; i++, c++, hole++) { POLY_CONTOUR1(&cpoly, c->points, c->num_points); gpc_polygon_clip(*hole?GPC_DIFF:GPC_UNION, (gpc_polygon *) &poly_out, (gpc_polygon *) &cpoly, (gpc_polygon *) &rpoly); POLY_SET(poly_out, &rpoly); } } #endif /* ********************************************************************************** * * ComputeCoordinates -- * ********************************************************************************** */ static void ComputeCoordinates(Item item, ZnBool force) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; int i, j; ZnPoint end_points[LINE_END_POINTS]; ZnPoint *points; int num_points, num_contours; ZnBBox bbox; int lw; ZnContour *c1, *c2; ZnBool *holes, *cw; ResetBBox(&item->item_bounding_box); SetRenderFlags(cv); /* * Try to reduce the contours if needed. This can be * switched off and replaced by an explicit reduction. */ if (cv->shape.num_contours == 1) { SET(cv->flags, REDUCED_BIT); } else if (ISCLEAR(cv->flags, REDUCED_BIT)) { #ifdef GPC ZnPoly poly; ReduceContours(&cv->shape, &poly); POLY_SET(&cv->shape, &poly); #endif SET(cv->flags, REDUCED_BIT); } num_contours = cv->shape.num_contours; if (num_contours == 0) { return; } #ifdef GPC if (cv->tristrip.num_strips) { gpc_free_tristrip(&cv->tristrip); } #endif /* * Allocate space for devices coordinates, the holes array is _NOT_ * duplicated. */ POLY_FREE(&cv->dev_shape); if (cv->shape.contours != &cv->shape.contour1) { cv->dev_shape.contours = (ZnContour *) ZnMalloc(num_contours*sizeof(ZnContour)); } else { cv->dev_shape.contours = &cv->dev_shape.contour1; cv->dev_shape.cw = &cv->dev_shape.cw1; } cv->dev_shape.num_contours = num_contours; c1 = cv->shape.contours; c2 = cv->dev_shape.contours; for (i = 0; i < cv->shape.num_contours; i++, c1++, c2++) { /* * Drop the last point of a contour if it is the same as * the first. */ if ((c1->points[0].x == c1->points[c1->num_points-1].x) && (c1->points[0].y == c1->points[c1->num_points-1].y) && (c1->num_points != 1)) { c1->num_points--; } c2->num_points = c1->num_points; /* * The device array is always one point larger to accomodate * the outline end point when CLOSED is specified. */ c2->points = (ZnPoint *) ZnMalloc((c1->num_points+1)*sizeof(ZnPoint)); /*printf("CC: \"%d\" num_points %d\n", item->id, c1->num_points);*/ } /* * Process all points, transforming coordinates. */ c1 = cv->shape.contours; c2 = cv->dev_shape.contours; for (j = 0; j < num_contours; j++, c1++, c2++) { ZnTransformPoints(wi->current_transfo, c1->points, c2->points, c1->num_points); if (ISSET(cv->flags, CLOSED_BIT)) { c2->points[c2->num_points] = c2->points[0]; } } lw = cv->line_width; if (ISSET(cv->flags, RELIEF_OK)) { holes = cv->shape.holes; c2 = cv->dev_shape.contours; if (!cv->dev_shape.cw) { cv->dev_shape.cw = (ZnBool *) ZnMalloc(num_contours*sizeof(ZnBool)); } cw = cv->dev_shape.cw; for (j = 0; j < num_contours; j++, c2++, holes++, cw++) { num_points = c2->num_points; points = c2->points; *cw = !TestCCW(points, num_points); if (*holes) { continue; } /* * Add to bounding box. */ GetPolygonReliefBBox(points, num_points, (*cw ^ *holes)?-lw:lw, &bbox); AddBBoxToBBox(&item->item_bounding_box, &bbox); } return; } holes = cv->shape.holes; c2 = cv->dev_shape.contours; for (j = 0; j < num_contours; j++, c2++, holes++) { if (!*holes) { AddPointsToBBox(&item->item_bounding_box, c2->points, c2->num_points); } } /* * Add the line width in all directions. * This overestimates the space needed to draw the polyline * but is simple. */ item->item_bounding_box.orig.x -= lw; item->item_bounding_box.orig.y -= lw; item->item_bounding_box.corner.x += lw; item->item_bounding_box.corner.y += lw; /* * Take care of miters, markers and arrows. */ holes = cv->shape.holes; c2 = cv->dev_shape.contours; for (j = 0; j < num_contours; j++, c2++, holes++) { if (*holes) { continue; } if (cv->join_style == JoinMiter) { ZnPoint miter_i, miter_o; for (i = c2->num_points-1, points = c2->points; i >= 3; i--, points++) { GetMiterPoints(points, points+1, points+2, lw, &miter_i, &miter_o); AddPointToBBox(&item->item_bounding_box, miter_i.x, miter_i.y); AddPointToBBox(&item->item_bounding_box, miter_o.x, miter_o.y); } } /* * Add the markers. */ if (ISSET(cv->flags, MARKER_OK)) { int w, h; ZnBBox bbox; Tk_SizeOfBitmap(wi->dpy, cv->marker, &w, &h); w = w/2 + 2; h = w/2 + 2; num_points = c2->num_points; for (i = 0, points = c2->points; i < num_points; i++, points++) { bbox.orig.x = points->x - w; bbox.orig.y = points->y - h; bbox.corner.x = points->x + w; bbox.corner.y = points->y + h; AddBBoxToBBox(&item->item_bounding_box, &bbox); } } /* * Process arrows. */ num_points = c2->num_points; points = c2->points; if (ISSET(cv->flags, FIRST_END_OK)) { GetLineEnd(&points[0], &points[1], lw, cv->cap_style, cv->first_end, end_points); AddPointsToBBox(&item->item_bounding_box, end_points, LINE_END_POINTS); } if (ISSET(cv->flags, LAST_END_OK)) { GetLineEnd(&points[num_points-1], &points[num_points-2], lw, cv->cap_style, cv->last_end, end_points); AddPointsToBBox(&item->item_bounding_box, end_points, LINE_END_POINTS); } } /* * Expand again the bounding box by one pixel in all * directions to take care of rounding errors. */ item->item_bounding_box.orig.x -= 1; item->item_bounding_box.orig.y -= 1; item->item_bounding_box.corner.x += 1; item->item_bounding_box.corner.y += 1; } /* ********************************************************************************** * * ToArea -- * Tell if the object is entirely outside (-1), * entirely inside (1) or in between (0). * ********************************************************************************** */ static int ToArea(Item item, ZnBBox *area, Tk_Uid tag_uid, int enclosed, ZnBool report) { CurveItem cv = (CurveItem) item; WidgetInfo *wi = item->wi; ZnBBox bbox; ZnPoint *points; ZnPoint end_points[LINE_END_POINTS]; int i, num_points, result=-1, result2; int width, height; ZnBool first_done = False; if (cv->dev_shape.num_contours == 0) { return -1; } /*printf("============== poly %d ==============\n", item->id);*/ if (ISSET(cv->flags, FILLED_OK)) { for (i = 0; i < cv->dev_shape.num_contours; i++) { ZnBool area_enclosed; num_points = cv->dev_shape.contours[i].num_points; points = cv->dev_shape.contours[i].points; if (!first_done) { first_done = True; result = PolygonInBBox(points, num_points, area, &area_enclosed); /*printf("contour %d, result %d, area_enclosed %d\n", i, result, area_enclosed);*/ if (cv->shape.holes[i] && area_enclosed) { return -1; } } else { result2 = PolygonInBBox(points, num_points, area, &area_enclosed); /*printf("contour %d, result %d, area_enclosed %d\n", i, result2, area_enclosed);*/ if (cv->shape.holes[i] && area_enclosed) { return -1; } if (result2 != result) { return 0; } } } } if (cv->line_width > 0) { for (i = 0; i < cv->dev_shape.num_contours; i++) { num_points = cv->dev_shape.contours[i].num_points; points = cv->dev_shape.contours[i].points; if (!first_done) { first_done = True; if (ISCLEAR(cv->flags, RELIEF_OK)) { result = PolylineInBBox(points, ISSET(cv->flags, CLOSED_BIT)?num_points+1:num_points, cv->line_width, cv->cap_style, cv->join_style, area); } else { result = PolygonReliefInBBox(points, ISSET(cv->flags, CLOSED_BIT)?num_points+1:num_points, (cv->dev_shape.cw[0]^cv->shape.holes[0])?-cv->line_width:cv->line_width, area); } if (result == 0) { return 0; } } else { if (ISCLEAR(cv->flags, RELIEF_OK)) { result2 = PolylineInBBox(points, ISSET(cv->flags, CLOSED_BIT)?num_points+1:num_points, cv->line_width, cv->cap_style, cv->join_style, area); } else { result2 = PolygonReliefInBBox(points, ISSET(cv->flags, CLOSED_BIT)?num_points+1:num_points, (cv->dev_shape.cw[0]^cv->shape.holes[0])?-cv->line_width:cv->line_width, area); } if (result2 != result) { return 0; } } } /* * Check line ends (only on first contour). */ points = cv->dev_shape.contours[0].points; num_points = cv->dev_shape.contours[0].num_points; if (ISSET(cv->flags, FIRST_END_OK)) { GetLineEnd(&points[0], &points[1], cv->line_width, cv->cap_style, cv->first_end, end_points); if (PolygonInBBox(end_points, LINE_END_POINTS, area, NULL) != result) { return 0; } } if (ISSET(cv->flags, LAST_END_OK)) { GetLineEnd(&points[num_points-1], &points[num_points-2], cv->line_width, cv->cap_style, cv->last_end, end_points); if (PolygonInBBox(end_points, LINE_END_POINTS, area, NULL) != result) { return 0; } } } /* * Last, check markers */ if (ISSET(cv->flags, MARKER_OK)) { for (i = 0; i < cv->dev_shape.num_contours; i++) { points = cv->dev_shape.contours[i].points; num_points = cv->dev_shape.contours[i].num_points; if (ISSET(cv->flags, FIRST_END_OK)) { num_points--; points++; } if (ISSET(cv->flags, LAST_END_OK)) { num_points--; } Tk_SizeOfBitmap(wi->dpy, cv->marker, &width, &height); for (; num_points > 0; num_points--, points++) { bbox.orig.x = points->x - (width+1)/2; bbox.orig.y = points->y - (height+1)/2; bbox.corner.x = bbox.orig.x + width; bbox.corner.y = bbox.orig.y + height; if (BBoxInBBox(&bbox, area) != result) { return 0; } } } } return result; } /* ********************************************************************************** * * Draw -- * ********************************************************************************** */ static void Draw(Item item) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; XGCValues values; int i, j, num_points=0, num2; unsigned int gc_mask; ZnPoint *points=NULL; XPoint *xpoints=NULL; if ((cv->dev_shape.num_contours == 0) || (ISCLEAR(cv->flags, FILLED_OK) && !cv->line_width && ISCLEAR(cv->flags, MARKER_OK))) { return; } /* * Fill if requested. */ if (ISSET(cv->flags, FILLED_OK)) { if (cv->grad_geom) { DrawPolygonGradient(wi, cv->grad_geom, cv->fill_color, &cv->dev_shape, &item->item_bounding_box); } else { values.foreground = ZnPixel(ZnColorGradientMidColor(wi->win, cv->fill_color)); gc_mask = GCFillStyle; if (cv->tile != ZnUnspecifiedImage) { /* Fill tiled */ Pixmap pmap = GetImagePixmap(wi->win, cv->tile_name, cv->tile, NULL); values.fill_style = FillTiled; values.tile = pmap; values.ts_x_origin = REAL_TO_INT(item->item_bounding_box.orig.x); values.ts_y_origin = REAL_TO_INT(item->item_bounding_box.orig.y); gc_mask |= GCTileStipXOrigin|GCTileStipYOrigin|GCTile; } else if (cv->fill_pattern != ZnUnspecifiedPattern) { /* Fill stippled */ values.fill_style = FillStippled; values.stipple = cv->fill_pattern; values.ts_x_origin = REAL_TO_INT(item->item_bounding_box.orig.x); values.ts_y_origin = REAL_TO_INT(item->item_bounding_box.orig.y); gc_mask |= GCTileStipXOrigin|GCTileStipYOrigin|GCStipple|GCForeground; } else { /* Fill solid */ values.fill_style = FillSolid; gc_mask |= GCForeground; } XChangeGC(wi->dpy, wi->gc, gc_mask, &values); #ifdef GPC if (cv->tristrip.num_strips == 0) { gpc_polygon_to_tristrip((gpc_polygon *) &cv->dev_shape, &cv->tristrip); } for (i = 0; i < cv->tristrip.num_strips; i++) { num_points = cv->tristrip.strip[i].num_vertices; points = (ZnPoint *) cv->tristrip.strip[i].vertex; ZnListAssertSize(wi->work_xpts, num_points); xpoints = (XPoint *) ZnListArray(wi->work_xpts); for (j = 0; j < num_points; j++) { xpoints[j].x = REAL_TO_INT(points[j].x); xpoints[j].y = REAL_TO_INT(points[j].y); } for (j = 0; j < num_points-2; j++) { XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, &xpoints[j], 3, Convex, CoordModeOrigin); } } #else num_points = cv->dev_shape.contours[0].num_points; points = cv->dev_shape.contours[0].points; ZnListAssertSize(wi->work_xpts, num_points); xpoints = (XPoint *) ZnListArray(wi->work_xpts); for (i = 0; i < num_points; i++) { xpoints[i].x = REAL_TO_INT(points[i].x); xpoints[i].y = REAL_TO_INT(points[i].y); } XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xpoints, num_points, Complex, CoordModeOrigin); #endif } } /* * Draw the lines between points */ if (cv->line_width) { ZnPoint end_points[LINE_END_POINTS]; XPoint xp[LINE_END_POINTS]; /* * Drawing with relief disables: ends, line style and line pattern. */ if (ISSET(cv->flags, RELIEF_OK)) { for (j = 0; j < cv->dev_shape.num_contours; j++) { num_points = cv->dev_shape.contours[j].num_points; points = cv->dev_shape.contours[j].points; if (ISSET(cv->flags, CLOSED_BIT)) { num_points++; } /*printf("Draw: num_points %d %g@%g %g@%g, cw %d\n", num_points, points[0].x, points[0].y, points[num_points-1].x, points[num_points-1].y, cv->dev_shape.cw[j]);*/ DrawPolygonRelief(wi, cv->relief, cv->gradient, points, num_points, (cv->dev_shape.cw[j]^cv->shape.holes[j])?-cv->line_width:cv->line_width); } } else { SetLineStyle(wi->dpy, wi->gc, cv->line_style); values.foreground = ZnPixel(cv->line_color); values.line_width = (cv->line_width == 1) ? 0 : cv->line_width; values.join_style = cv->join_style; values.cap_style = cv->cap_style; if (cv->line_pattern == ZnUnspecifiedPattern) { values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCLineWidth|GCJoinStyle|GCCapStyle|GCForeground, &values); } else { values.fill_style = FillStippled; values.stipple = cv->line_pattern; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCStipple|GCLineWidth|GCJoinStyle|GCCapStyle|GCForeground, &values); } for (j = 0; j < cv->dev_shape.num_contours; j++) { num2 = num_points = cv->dev_shape.contours[j].num_points; points = cv->dev_shape.contours[j].points; if (ISSET(cv->flags, CLOSED_BIT)) { num_points++; } ZnListAssertSize(wi->work_xpts, num_points); xpoints = (XPoint *) ZnListArray(wi->work_xpts); for (i = 0; i < num2; i++) { xpoints[i].x = REAL_TO_INT(points[i].x); xpoints[i].y = REAL_TO_INT(points[i].y); } if (ISSET(cv->flags, CLOSED_BIT)) { xpoints[num2] = xpoints[0]; } XDrawLines(wi->dpy, wi->draw_buffer, wi->gc, xpoints, num_points, CoordModeOrigin); } if (ISSET(cv->flags, FIRST_END_OK)) { GetLineEnd(&points[0], &points[1], cv->line_width, cv->cap_style, cv->first_end, end_points); for (i = 0; i < LINE_END_POINTS; i++) { xp[i].x = end_points[i].x; xp[i].y = end_points[i].y; } XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xp, LINE_END_POINTS, Nonconvex, CoordModeOrigin); } if (ISSET(cv->flags, LAST_END_OK)) { GetLineEnd(&points[num_points-1], &points[num_points-2], cv->line_width, cv->cap_style, cv->last_end, end_points); for (i = 0; i < LINE_END_POINTS; i++) { xp[i].x = end_points[i].x; xp[i].y = end_points[i].y; } XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xp, LINE_END_POINTS, Nonconvex, CoordModeOrigin); } } } /* * Draw the marks at each point. If arrows are specified or * if last point join first point suppress markers at end points. */ if (ISSET(cv->flags, MARKER_OK)) { int width, h_width, height, h_height; ZnPoint ptmp; Tk_SizeOfBitmap(wi->dpy, cv->marker, &width, &height); h_width = (width+1)/2; h_height = (height+1)/2; values.fill_style = FillStippled; values.stipple = cv->marker; values.foreground = ZnPixel(cv->marker_color); XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCStipple|GCForeground, &values); for (j = 0; j < cv->dev_shape.num_contours; j++) { num_points = cv->dev_shape.contours[j].num_points; points = cv->dev_shape.contours[j].points; ZnListAssertSize(wi->work_xpts, num_points); xpoints = (XPoint *) ZnListArray(wi->work_xpts); for (i = 0; i < num_points; i++) { xpoints[i].x = REAL_TO_INT(points[i].x); xpoints[i].y = REAL_TO_INT(points[i].y); } if (ISSET(cv->flags, FIRST_END_OK)) { num_points--; points++; } if (ISSET(cv->flags, LAST_END_OK)) { num_points--; } for (; num_points > 0; num_points--, points++) { ptmp.x = points->x - h_width; ptmp.y = points->y - h_height; values.ts_x_origin = ptmp.x; values.ts_y_origin = ptmp.y; XChangeGC(wi->dpy, wi->gc, GCTileStipXOrigin|GCTileStipYOrigin|GCForeground, &values); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, ptmp.x, ptmp.y, width, height); } } } } /* ********************************************************************************** * * Render -- * ********************************************************************************** */ static void Render(Item item) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; XColor *color = ZnColorGradientMidColor(wi->win, cv->fill_color); ArtPixBuf *pixbuf = NULL; if (cv->fill_svp != NULL) { if (strlen(cv->tile_name) != 0) { pixbuf = GetImagePixbuf(wi->win, cv->tile_name, cv->tile); } ITEM_P.RenderSVP(wi, cv->fill_svp, color->red, color->green, color->blue, cv->fill_alpha & 0xff, item->item_bounding_box.orig.x, item->item_bounding_box.orig.y, pixbuf); } if (cv->outline_svp != NULL) { ITEM_P.RenderSVP(wi, cv->outline_svp, cv->line_color->red, cv->line_color->green, cv->line_color->blue, cv->line_alpha & 0xff, 0, 0, NULL); } } /* ********************************************************************************** * * IsSensitive -- * ********************************************************************************** */ static ZnBool IsSensitive(Item item, int item_part) { return (ISSET(item->flags, SENSITIVE_BIT) && item->parent->class->IsSensitive(item->parent, ZN_NO_PART)); } /* ********************************************************************************** * * Pick -- * ********************************************************************************** */ static double Pick(Item item, ZnPoint *p, Item start_item, int aperture, Item *a_item, int *part) { CurveItem cv = (CurveItem) item; WidgetInfo *wi = item->wi; ZnBBox bbox; double dist=1.0e40, new_dist; ZnPoint *points; ZnPoint end_points[LINE_END_POINTS]; int num_points; int width, height; int i; if (cv->dev_shape.num_contours == 0) { return dist; } /*printf("Pick in curve\n");*/ if (ISSET(cv->flags, FILLED_OK)) { double hole_dist=1.0e40, new_hole_dist; /* * Check all contours. Compute distance to holes * in the same pass. */ for (i = 0; i < cv->dev_shape.num_contours; i++) { if (cv->shape.holes[i]) { new_hole_dist = PolygonToPointDist(cv->dev_shape.contours[i].points, cv->dev_shape.contours[i].num_points, p); if (new_hole_dist < hole_dist) { hole_dist = new_hole_dist; } } else { new_dist = PolygonToPointDist(cv->dev_shape.contours[i].points, cv->dev_shape.contours[i].num_points, p); if (new_dist < dist) { dist = new_dist; } } } if ((dist <= 0.0) && (hole_dist >= 0.0)) { return 0.0; } if (hole_dist < 0.0) { hole_dist = -hole_dist; } if (dist <= 0.0) { dist = hole_dist; } else if (hole_dist < dist) { dist = hole_dist; } } if (cv->line_width > 0) { /* * Check all contours. */ for (i = 0; i < cv->dev_shape.num_contours; i++) { points = cv->dev_shape.contours[i].points; num_points = cv->dev_shape.contours[i].num_points; if (ISCLEAR(cv->flags, RELIEF_OK)) { new_dist = PolylineToPointDist(points, ISSET(cv->flags, CLOSED_BIT)?num_points+1:num_points, cv->line_width, cv->cap_style, cv->join_style, p); if (new_dist < dist) { dist = new_dist; } if (dist <= 0.0) { return 0.0; } } else { new_dist = PolygonReliefToPointDist(points, ISSET(cv->flags, CLOSED_BIT)?num_points+1:num_points, (cv->dev_shape.cw[0]^cv->shape.holes[0])?-cv->line_width:cv->line_width, p); if (new_dist < dist) { dist = new_dist; } if (dist <= 0.0) { return 0.0; } } } } /* * Line ends are checked only on the first contour. */ points = cv->dev_shape.contours[0].points; num_points = cv->dev_shape.contours[0].num_points; /* * Check line ends. */ if (ISSET(cv->flags, FIRST_END_OK)) { GetLineEnd(&points[0], &points[1], cv->line_width, cv->cap_style, cv->first_end, end_points); new_dist = PolygonToPointDist(end_points, LINE_END_POINTS, p); if (new_dist < dist) { dist = new_dist; } if (dist <= 0.0) { return 0.0; } } if (ISSET(cv->flags, LAST_END_OK)) { GetLineEnd(&points[num_points-1], &points[num_points-2], cv->line_width, cv->cap_style, cv->last_end, end_points); new_dist = PolygonToPointDist(end_points, LINE_END_POINTS, p); if (new_dist < dist) { dist = new_dist; } if (dist <= 0.0) { return 0.0; } } /* * Last, check markers on all contours. */ if (ISSET(cv->flags, MARKER_OK)) { for (i = 0; i < cv->dev_shape.num_contours; i++) { points = cv->dev_shape.contours[i].points; num_points = cv->dev_shape.contours[i].num_points; if (ISSET(cv->flags, FIRST_END_OK)) { num_points--; points++; } if (ISSET(cv->flags, LAST_END_OK)) { num_points--; } Tk_SizeOfBitmap(wi->dpy, cv->marker, &width, &height); for (; num_points > 0; num_points--, points++) { bbox.orig.x = points->x - (width+1)/2; bbox.orig.y = points->y - (height+1)/2; bbox.corner.x = bbox.orig.x + width; bbox.corner.y = bbox.orig.y + height; new_dist = RectangleToPointDist(&bbox, p); if (new_dist < dist) { dist = new_dist; } if (dist <= 0.0) { return 0.0; } } } } return dist; } /* ********************************************************************************** * * PostScript -- * ********************************************************************************** */ static void PostScript(Item item, PostScriptInfo ps_info) { } /* ********************************************************************************** * * GetClipVertices -- * Get the clipping shape. * ********************************************************************************** */ static ZnBool GetClipVertices(Item item, ZnPoly *poly) { CurveItem cv = (CurveItem) item; POLY_INIT(poly); if (cv->dev_shape.num_contours == 1) { POLY_CONTOUR1(poly, cv->dev_shape.contours[0].points, cv->dev_shape.contours[0].num_points); } else if (cv->dev_shape.num_contours > 1) { poly->num_contours = cv->dev_shape.num_contours; poly->contours = cv->dev_shape.contours; } return False; } /* ********************************************************************************** * * Coords -- * Return or edit the item vertices. * ********************************************************************************** */ static int Coords(Item item, int contour, int index, int cmd, ZnPoint **pts, int *num_pts) { CurveItem cv = (CurveItem) item; int i; ZnContour *c; /*printf("contour %d, num_pts %d, index %d, cmd %d\n", contour, *num_pts, index, cmd);*/ if (contour < 0) { contour += cv->shape.num_contours; } if ((contour < 0) || (contour >= cv->shape.num_contours)) { Tcl_AppendResult(item->wi->interp, " curve contour index out of range", NULL); return ZN_ERROR; } c = &cv->shape.contours[contour]; if ((cmd == COORDS_REPLACE) || (cmd == COORDS_REPLACE_ALL)) { if (*num_pts == 0) { Tcl_AppendResult(item->wi->interp, " coords command need at least 1 point on curves", NULL); return ZN_ERROR; } if (cmd == COORDS_REPLACE_ALL) { replace_all: if (c->points) { ZnFree(c->points); } c->points = (ZnPoint *) ZnMalloc(*num_pts*sizeof(ZnPoint)); c->num_points = *num_pts; memcpy(c->points, *pts, *num_pts*sizeof(ZnPoint)); } else { if (c->num_points == 0) { edit_err: Tcl_AppendResult(item->wi->interp, " coords command cannot edit empty curve contour", NULL); return ZN_ERROR; } if (index < 0) { index += c->num_points; } if ((index < 0) || (index >= c->num_points)) { range_err: Tcl_AppendResult(item->wi->interp, " coord index out of range", NULL); return ZN_ERROR; } c->points[index] = (*pts)[0]; } CLEAR(cv->flags, REDUCED_BIT); ITEM.Invalidate(item, ZN_COORDS_FLAG); } else if ((cmd == COORDS_READ) || (cmd == COORDS_READ_ALL)) { if (c->num_points == 0) { *num_pts = 0; *pts = NULL; return ZN_OK; } if (cmd == COORDS_READ_ALL) { *num_pts = c->num_points; *pts = c->points; } else { if (index < 0) { index += c->num_points; } if ((index < 0) || (index >= c->num_points)) { goto range_err; } *num_pts = 1; *pts = &c->points[index]; } } else if ((cmd == COORDS_ADD) || (cmd == COORDS_ADD_LAST)) { if (c->num_points == 0) { goto replace_all; } if (cmd == COORDS_ADD_LAST) { index = c->num_points; } if (index < 0) { index += c->num_points; } if ((index < 0) || (index > c->num_points)) { goto range_err; } c->points = (ZnPoint *) ZnRealloc(c->points, (c->num_points+*num_pts)*sizeof(ZnPoint)); /* * Make a hole if needed. */ for (i = c->num_points-1; i >= index; i--) { c->points[i+*num_pts] = c->points[i]; } for (i = 0; i < *num_pts; i++, index++) { c->points[index] = (*pts)[i]; } c->num_points += *num_pts; CLEAR(cv->flags, REDUCED_BIT); ITEM.Invalidate(item, ZN_COORDS_FLAG); } else if (cmd == COORDS_REMOVE) { if (c->num_points == 0) { goto edit_err; } if (index < 0) { index += c->num_points; } if ((index < 0) || (index >= c->num_points)) { goto range_err; } c->num_points--; if (index != c->num_points) { for (i = index; i < c->num_points; i++) { c->points[i] = c->points[i+1]; } } CLEAR(cv->flags, REDUCED_BIT); ITEM.Invalidate(item, ZN_COORDS_FLAG); } return ZN_OK; } /* ********************************************************************************** * * Contour -- * Perform geometric operations on curve contours. * ********************************************************************************** */ #ifdef GPC static void Contour(Item item, int cmd, ZnPoly *poly) { CurveItem cv = (CurveItem) item; ZnPoly rpoly; POLY_INIT(&rpoly); gpc_polygon_clip(cmd, (gpc_polygon *) &cv->shape, (gpc_polygon *) poly, (gpc_polygon *) &rpoly); POLY_SET(&cv->shape, &rpoly); /*printf("cmd %d, num contours %d, num vertices %d\n", cmd, cv->shape.num_contours, cv->shape.contours[0].num_points);*/ ITEM.Invalidate(item, ZN_COORDS_FLAG); } #endif /* ********************************************************************************** * * PickVertex -- * Return in 'vertex' the vertex closest to p and in 'o_vertex' the * opposite vertex on the closest edge, if such an edge exists or -1 * in the other case. * ********************************************************************************** */ static void PickVertex(Item item, ZnPoint *p, int *contour, int *vertex, int *o_vertex) { CurveItem cv = (CurveItem) item; int i, j, k, num_points; ZnPoint *points; ZnReal dist=1.0e40, new_dist, dist2; *contour = *vertex = *o_vertex = -1; if ((cv->line_width > 0) || ISSET(cv->flags, FILLED_OK) || ISSET(cv->flags, MARKER_OK)) { /* * Check all contours. */ for (i = 0; i < cv->dev_shape.num_contours; i++) { points = cv->dev_shape.contours[i].points; num_points = cv->dev_shape.contours[i].num_points; for (j = 0; j < num_points; j++) { new_dist = hypot(points[j].x - p->x, points[j].y - p->y); if (new_dist < dist) { dist = new_dist; *contour = i; *vertex = j; } } /* * If the closest vertex is in the current contour update * the opposite vertex. */ if (i == *contour) { j = (*vertex+1) % num_points; new_dist = LineToPointDist(&points[*vertex], &points[j], p); k = ((unsigned)(*vertex-1)) % num_points; dist2 = LineToPointDist(&points[*vertex], &points[k], p); if (dist2 < new_dist) { *o_vertex = k; } else { *o_vertex = j; } } } } } /* ********************************************************************************** * * Exported functions struct -- * ********************************************************************************** */ static ItemClassStruct CURVE_ITEM_CLASS = { sizeof(CurveItemStruct), False, /* has_fields */ 0, /* num_parts */ False, /* has_anchors */ "curve", cv_attrs, Init, Clone, Destroy, Configure, Query, NULL, /* GetFieldSet */ NULL, /* GetAnchor */ GetClipVertices, Coords, NULL, /* InsertChars */ NULL, /* DeleteChars */ NULL, /* Cursor */ NULL, /* Index */ NULL, /* Part */ NULL, /* Selection */ #ifdef GPC Contour, #else NULL, #endif ComputeCoordinates, ToArea, Draw, Render, IsSensitive, Pick, PickVertex, /* PickVertex */ PostScript }; ZnItemClassId ZnCurve = (ZnItemClassId) &CURVE_ITEM_CLASS;