diff options
-rw-r--r-- | Bezier.c | 1138 |
1 files changed, 1138 insertions, 0 deletions
diff --git a/Bezier.c b/Bezier.c new file mode 100644 index 0000000..b966593 --- /dev/null +++ b/Bezier.c @@ -0,0 +1,1138 @@ +/* + * Bezier.c -- Implementation of bezier item. + * + * Authors : Patrick Lecoanet. + * Creation date : Fri May 5 12:33:32 2000 + * + * $Id$ + */ + +/* + * Copyright (c) 1993 - 2000 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" + +#include <ctype.h> +#include <malloc.h> + + +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 CLOSED_BIT 1<<1 /* If the item should be closed */ +#define CCW 1<<2 /* Tell if the controls are described in + * clockwise or ccw order. */ +#define FIRST_END_OK 1<<3 +#define LAST_END_OK 1<<4 +#define FILLED_OK 1<<5 +#define RELIEF_OK 1<<6 + + +/* + ********************************************************************************** + * + * Specific Bezier item record + * + ********************************************************************************** + */ +typedef struct _BezierItemStruct { + ItemStruct header; + + /* Public data */ + ZnList points; + unsigned int flags; + 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; + 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; + char *tile_name; + + /* Private data */ + ZnImage tile; + ZnList dev_points; + ZnColorGradient gradient; +} BezierItemStruct, *BezierItem; + + +static ZnAttrConfig bz_attrs[] = { + { ZN_CONFIG_CAP_STYLE, "-capstyle", NULL, + Tk_Offset(BezierItemStruct, cap_style), 0, + ZN_COORDS_FLAG, False }, + { ZN_CONFIG_BOOL, "-composerotation", NULL, + Tk_Offset(BezierItemStruct, header.flags), COMPOSE_ROTATION_BIT, + ZN_COORDS_FLAG, False }, + { ZN_CONFIG_BOOL, "-composescale", NULL, + Tk_Offset(BezierItemStruct, header.flags), COMPOSE_SCALE_BIT, + ZN_COORDS_FLAG, False }, + { ZN_CONFIG_GRADIENT_COLOR, "-fillcolor", NULL, + Tk_Offset(BezierItemStruct, fill_color), 0, + ZN_DRAW_FLAG|ZN_BORDER_FLAG, False }, + { ZN_CONFIG_PATTERN, "-fillpattern", NULL, + Tk_Offset(BezierItemStruct, fill_pattern), 0, ZN_DRAW_FLAG, False }, + { ZN_CONFIG_BOOL, "-filled", NULL, + Tk_Offset(BezierItemStruct, flags), FILLED_BIT, ZN_COORDS_FLAG, False }, + { ZN_CONFIG_LINE_END, "-firstend", NULL, + Tk_Offset(BezierItemStruct, first_end), 0, ZN_COORDS_FLAG, False }, + { ZN_CONFIG_GRADIENT_GEOM, "-gradient", NULL, + Tk_Offset(BezierItemStruct, grad_geom), 0, ZN_DRAW_FLAG, False }, + { ZN_CONFIG_LINE_END, "-lastend", NULL, + Tk_Offset(BezierItemStruct, last_end), 0, ZN_COORDS_FLAG, False }, + { ZN_CONFIG_COLOR, "-linecolor", NULL, + Tk_Offset(BezierItemStruct, line_color), 0, + ZN_DRAW_FLAG, False }, + { ZN_CONFIG_PATTERN, "-linepattern", NULL, + Tk_Offset(BezierItemStruct, line_pattern), 0, ZN_DRAW_FLAG, False }, + { ZN_CONFIG_LINE_STYLE, "-linestyle", NULL, + Tk_Offset(BezierItemStruct, line_style), 0, ZN_DRAW_FLAG, False }, + { ZN_CONFIG_DIM, "-linewidth", NULL, + Tk_Offset(BezierItemStruct, line_width), 0, ZN_COORDS_FLAG, False }, + { ZN_CONFIG_PRI, "-priority", NULL, + Tk_Offset(BezierItemStruct, header.priority), 0, + ZN_DRAW_FLAG|ZN_REPICK_FLAG, False }, + { ZN_CONFIG_RELIEF, "-relief", NULL, Tk_Offset(BezierItemStruct, relief), 0, + ZN_COORDS_FLAG, False }, + { ZN_CONFIG_BOOL, "-sensitive", NULL, + Tk_Offset(BezierItemStruct, header.flags), SENSITIVE_BIT, + ZN_REPICK_FLAG, False }, + { ZN_CONFIG_TAGS, "-tags", NULL, + Tk_Offset(BezierItemStruct, header.tags), 0, 0, False }, + { ZN_CONFIG_IMAGE, "-tile", NULL, + Tk_Offset(BezierItemStruct, tile_name), 0, + ZN_DRAW_FLAG|ZN_TILE_FLAG, False }, + { ZN_CONFIG_BOOL, "-visible", NULL, + Tk_Offset(BezierItemStruct, header.flags), VISIBLE_BIT, + ZN_DRAW_FLAG|ZN_REPICK_FLAG|ZN_VIS_FLAG, False }, + + { ZN_CONFIG_END, NULL, NULL, 0, 0, 0 } +}; + + +/* + ********************************************************************************** + * + * BzTileChange -- + * + ********************************************************************************** + */ +static void +BzTileChange(ClientData client_data, + int x, + int y, + int width, + int height, + int image_width, + int image_height) +{ + BezierItem bz = (BezierItem) client_data; + + InvalidateImage(bz->tile_name); + ITEM.Invalidate((Item) bz, ZN_COORDS_FLAG); +} + + +/* + ********************************************************************************** + * + * Init -- + * + ********************************************************************************** + */ +static int +Init(Item item, + int *argc, + Arg **args) +{ + WidgetInfo *wi = item->wi; + BezierItem bz = (BezierItem) item; + Arg *elems; + int i, result, num_elems; + ZnPoint p; +#ifdef PTK + LangFreeProc *freeProc = NULL; +#endif + + bz->dev_points = NULL; + bz->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); + item->priority = DEFAULT_BEZIER_PRIORITY; + + if (*argc < 1) { + Tcl_AppendResult(wi->interp, " bezier coords expected", NULL); + return ZN_ERROR; + } + result = Lang_SplitList(wi->interp, (*args)[0], &num_elems, &elems, &freeProc); + if ((result == ZN_ERROR) || ((num_elems%2) != 0)) { + bz_error: +#ifdef PTK + if (elems != NULL && freeProc) { + (*freeProc)(num_elems, elems); + } +#endif + Tcl_AppendResult(wi->interp, " malformed bezier coords", NULL); + return ZN_ERROR; + } + + bz->points = ZnListNew(num_elems/2, sizeof(ZnPoint)); + for (i = 0; i < num_elems; i += 2) { + if (Tcl_GetDouble(wi->interp, elems[i], &p.x) == ZN_ERROR) { + bz_error2: +#ifndef PTK + Tcl_Free((char *) elems); +#endif + ZnListFree(bz->points); + bz->points = NULL; + goto bz_error; + } + if (Tcl_GetDouble(wi->interp, elems[i+1], &p.y) == ZN_ERROR) { + goto bz_error2; + } + ZnListAdd(bz->points, &p, ZnListTail); + } + (*args)++; + (*argc)--; +#ifndef PTK + Tcl_Free((char *) elems); +#else + if (freeProc) { + (*freeProc)(num_elems, elems); + } +#endif + + CLEAR(bz->flags, FILLED_BIT); + bz->first_end = NULL; + bz->last_end = NULL; + bz->line_style = LINE_SIMPLE; + bz->relief = RELIEF_FLAT; + bz->line_width = 1; + bz->tile_name = ""; + bz->tile = ZnUnspecifiedImage; + bz->fill_pattern = ZnUnspecifiedPattern; + bz->line_pattern = ZnUnspecifiedPattern; + bz->cap_style = CapRound; + + bz->fill_color = ZnGetColorGradient(wi->interp, wi->win, + ZnNameOfColor(wi->fore_color)); + bz->line_color = ZnGetColorByValue(wi->win, wi->fore_color); + bz->grad_geom = NULL; + + return ZN_OK; +} + + +/* + ********************************************************************************** + * + * Clone -- + * + ********************************************************************************** + */ +static void +Clone(Item item) +{ + BezierItem bz = (BezierItem) item; + WidgetInfo *wi = item->wi; + char *text; + + bz->dev_points = NULL; + + if (bz->gradient) { + bz->gradient = ZnGetColorGradientByValue(bz->gradient); + } + if (bz->points) { + bz->points = ZnListDuplicate(bz->points); + } + if (bz->line_pattern != ZnUnspecifiedPattern) { + bz->line_pattern = Tk_GetBitmap(wi->interp, wi->win, + Tk_NameOfBitmap(wi->dpy, bz->line_pattern)); + } + if (bz->first_end) { + LineEndDuplicate(bz->first_end); + } + if (bz->last_end) { + LineEndDuplicate(bz->last_end); + } + if (strlen(bz->tile_name) != 0) { + text = ZnMalloc((strlen(bz->tile_name) + 1) * sizeof(char)); + strcpy(text, bz->tile_name); + bz->tile_name = text; + bz->tile = Tk_GetImage(wi->interp, wi->win, bz->tile_name, + BzTileChange, (ClientData) bz); + } + if (bz->fill_pattern != ZnUnspecifiedPattern) { + bz->fill_pattern = Tk_GetBitmap(wi->interp, wi->win, + Tk_NameOfBitmap(wi->dpy, bz->fill_pattern)); + } + bz->line_color = ZnGetColorByValue(wi->win, bz->line_color); + bz->fill_color = ZnGetColorGradientByValue(bz->fill_color); + if (bz->grad_geom) { + bz->grad_geom = GradientGeomDuplicate(bz->grad_geom); + } +} + + +/* + ********************************************************************************** + * + * Destroy -- + * + ********************************************************************************** + */ +static void +Destroy(Item item) +{ + WidgetInfo *wi = item->wi; + BezierItem bz = (BezierItem) item; + + if (bz->points) { + ZnListFree(bz->points); + } + if (bz->dev_points) { + ZnListFree(bz->dev_points); + } + if (bz->first_end) { + LineEndDelete(bz->first_end); + } + if (bz->last_end) { + LineEndDelete(bz->last_end); + } + if (bz->gradient) { + ZnFreeColorGradient(bz->gradient); + } + if (bz->tile != ZnUnspecifiedImage) { + Tk_FreeImage(bz->tile); + bz->tile = ZnUnspecifiedImage; + } + if (strlen(bz->tile_name) != 0) { + ZnFree(bz->tile_name); + } + if (bz->line_pattern != ZnUnspecifiedPattern) { + Tk_FreeBitmap(wi->dpy, bz->line_pattern); + } + if (bz->fill_pattern != ZnUnspecifiedPattern) { + Tk_FreeBitmap(wi->dpy, bz->fill_pattern); + } + ZnFreeColorGradient(bz->fill_color); + ZnFreeColor(bz->line_color); + if (bz->grad_geom) { + GradientGeomDelete(bz->grad_geom); + } +} + + +/* + ********************************************************************************** + * + * Setup flags to control the precedence between the + * graphical attributes. + * + ********************************************************************************** + */ +static void +SetRenderFlags(BezierItem bz) +{ + ASSIGN(bz->flags, FILLED_OK, + ISSET(bz->flags, FILLED_BIT) && (ZnListSize(bz->points) > 2)); + + ASSIGN(bz->flags, RELIEF_OK, + (bz->relief != RELIEF_FLAT) && (ZnListSize(bz->points) > 1) && + (bz->line_width > 1)); + + ASSIGN(bz->flags, FIRST_END_OK, + (bz->first_end != NULL) && (ZnListSize(bz->points) > 1) && + ISCLEAR(bz->flags, FILLED_BIT) && bz->line_width && + ISCLEAR(bz->flags, RELIEF_OK)); + + ASSIGN(bz->flags, LAST_END_OK, + (bz->last_end != NULL) && (ZnListSize(bz->points) > 1) && + ISCLEAR(bz->flags, FILLED_BIT) && bz->line_width && + ISCLEAR(bz->flags, RELIEF_OK)); +} + + +/* + ********************************************************************************** + * + * Configure -- + * + ********************************************************************************** + */ +static int +Configure(Item item, + int argc, + ZnAttrList argv, + int *flags) +{ + WidgetInfo *wi = item->wi; + BezierItem bz = (BezierItem) item; + int status = ZN_OK; + + status = ITEM_P.ConfigureAttributes((char *) item, -1, argc, argv, flags); + + if (bz->gradient && + (ISSET(*flags, ZN_BORDER_FLAG) || (bz->relief == RELIEF_FLAT))) { + ZnFreeColorGradient(bz->gradient); + bz->gradient = NULL; + } + if ((bz->relief != RELIEF_FLAT) && !bz->gradient) { + bz->gradient = ZnGetReliefGradient(wi->interp, wi->win, + ZnNameOfColor(ZnColorGradientMidColor(wi->win, bz->fill_color))); + } + if (ISSET(*flags, ZN_TILE_FLAG)) { + Tk_Image tile; + + if (strcmp(bz->tile_name, "") != 0) { + tile = Tk_GetImage(wi->interp, wi->win, bz->tile_name, + BzTileChange, (ClientData) bz); + if (tile == NULL) { + /* + * The name will not be in sync with the image in + * this case. + */ + status = ZN_ERROR; + } + } + else { + tile = ZnUnspecifiedImage; + } + if (bz->tile != ZnUnspecifiedImage) { + Tk_FreeImage(bz->tile); + } + bz->tile = tile; + } + + return status; +} + + +/* + ********************************************************************************** + * + * Query -- + * + ********************************************************************************** + */ +static int +Query(Item item, + int argc, + ZnAttrList 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]; + 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. */ +} + + +/* + ********************************************************************************** + * + * ComputeCoordinates -- + * + ********************************************************************************** + */ +static void +ComputeCoordinates(Item item, + ZnBool force) +{ + WidgetInfo *wi = item->wi; + BezierItem bz = (BezierItem) item; + ZnPoint *points; + ZnPoint end_points[LINE_END_POINTS]; + ZnPoint *dev_points; + int num_points; + ZnBBox bbox; + int lw; + + ResetBBox(&item->item_bounding_box); + if (bz->points == NULL) { + return; + } + + SetRenderFlags(bz); + + points = (ZnPoint *) ZnListArray(bz->points); + num_points = ZnListSize(bz->points); + + /* + * Allocate space for devices coordinates + */ + if (bz->dev_points == NULL) { + bz->dev_points = ZnListNew(num_points, sizeof(ZnPoint)); + } + ZnListAssertSize(bz->dev_points, num_points); + dev_points = (ZnPoint *) ZnListArray(bz->dev_points); + + /* + * Compute device coordinates. + */ + ZnTransformPoints(wi->current_transfo, points, dev_points, num_points); + + lw = bz->line_width; + + if (ISSET(bz->flags, RELIEF_OK)) { + ASSIGN(bz->flags, CCW, TestCCW(dev_points, num_points)); + + /* + * Compute the bounding box. + */ + GetPolygonReliefBBox(dev_points, num_points, + ISCLEAR(bz->flags, CCW)?-lw:lw, &bbox); + AddBBoxToBBox(&item->item_bounding_box, &bbox); + return; + } + + /* + * Compute the bounding box. + */ + AddPointsToBBox(&item->item_bounding_box, dev_points, num_points); + + /* + * Add the line width in all directions. + * This overestimates the space needed to draw the polyline + * but is simple. This is even more true for smoothed polygons but is + * even faster. + */ + 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; + + /* + * Process arrows. + */ + if (ISSET(bz->flags, FIRST_END_OK)) { + GetLineEnd(&dev_points[0], &dev_points[1], lw, bz->cap_style, + bz->first_end, end_points); + AddPointsToBBox(&item->item_bounding_box, end_points, LINE_END_POINTS); + } + if (ISSET(bz->flags, LAST_END_OK)) { + GetLineEnd(&dev_points[num_points-1], &dev_points[num_points-2], + lw, bz->cap_style, bz->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) +{ + BezierItem bz = (BezierItem) item; + WidgetInfo *wi = item->wi; + ZnPoint *points; + ZnPoint end_points[LINE_END_POINTS]; + int num_points, result, result2; + int lw = bz->line_width; + + if (bz->dev_points == NULL) { + return -1; + } + + /*printf("============== bezier %d ==============\n", item->id);*/ + GetBezierPath(bz->dev_points, wi->work_pts); + points = (ZnPoint *) ZnListArray(wi->work_pts); + num_points = ZnListSize(wi->work_pts); + + if (ISSET(bz->flags, FILLED_OK)) { + result = PolygonInBBox(points, num_points, area, NULL); + if (result == 0) { + return 0; + } + } + + if (lw > 0) { + if (ISCLEAR(bz->flags, RELIEF_OK)) { + result2 = PolylineInBBox(points, num_points, lw, + bz->cap_style, JoinRound, area); + } + else { + result2 = PolygonReliefInBBox(points, num_points, + ISCLEAR(bz->flags, CCW)?-lw:lw, area); + } + if (ISCLEAR(bz->flags, FILLED_OK)) { + if (result2 == 0) { + return 0; + } + result = result2; + } + else if (result2 != result) { + return 0; + } + + /* + * Check line ends. + */ + if (ISSET(bz->flags, FIRST_END_OK)) { + GetLineEnd(&points[0], &points[1], lw, bz->cap_style, + bz->first_end, end_points); + if (PolygonInBBox(end_points, LINE_END_POINTS, area, NULL) != result) { + return 0; + } + } + if (ISSET(bz->flags, LAST_END_OK)) { + GetLineEnd(&points[num_points-1], &points[num_points-2], lw, + bz->cap_style, bz->last_end, end_points); + if (PolygonInBBox(end_points, LINE_END_POINTS, area, NULL) != result) { + return 0; + } + } + } + + return result; +} + + +/* + ********************************************************************************** + * + * Draw -- + * + ********************************************************************************** + */ +static void +Draw(Item item) +{ + WidgetInfo *wi = item->wi; + BezierItem bz = (BezierItem) item; + XGCValues values; + int i, num_points; + unsigned int gc_mask; + ZnPoint *points; + XPoint *xpoints = NULL; + int lw = bz->line_width; + + if (bz->dev_points == NULL) { + return; + } + + GetBezierPath(bz->dev_points, wi->work_pts); + points = (ZnPoint *) ZnListArray(wi->work_pts); + num_points = ZnListSize(wi->work_pts); + + /* + * Fill if requested. + */ + if (ISSET(bz->flags, FILLED_OK)) { + if (bz->grad_geom) { + ZnPoly poly; + POLY_CONTOUR1(&poly, points, num_points); + DrawPolygonGradient(wi, bz->grad_geom, bz->fill_color, &poly, + &item->item_bounding_box); + } + else { + values.foreground = ZnPixel(ZnColorGradientMidColor(wi->win, bz->fill_color)); + gc_mask = GCFillStyle; + if (bz->tile != ZnUnspecifiedImage) { /* Fill tiled */ + Pixmap pmap = GetImagePixmap(wi->win, bz->tile_name, bz->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 (bz->fill_pattern != ZnUnspecifiedPattern) { /* Fill stippled */ + values.fill_style = FillStippled; + values.stipple = bz->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); + + if (!xpoints) { + 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); + } + } + + /* + * Draw the lines between points + */ + if (lw) { + ZnPoint end_points[LINE_END_POINTS]; + XPoint xp[LINE_END_POINTS]; + + /* + * Drawing with relief disables: ends, line style and line pattern. + */ + if (ISSET(bz->flags, RELIEF_OK)) { + DrawPolygonRelief(wi, bz->relief, bz->gradient, + points, num_points, ISCLEAR(bz->flags, CCW)?-lw:lw); + } + else { + SetLineStyle(wi->dpy, wi->gc, bz->line_style); + values.foreground = ZnPixel(bz->line_color); + values.line_width = (lw == 1) ? 0 : lw; + values.join_style = JoinRound; + values.cap_style = bz->cap_style; + if (bz->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 = bz->line_pattern; + XChangeGC(wi->dpy, wi->gc, + GCFillStyle|GCStipple|GCLineWidth|GCJoinStyle|GCCapStyle|GCForeground, + &values); + } + if (!xpoints) { + 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); + } + } + XDrawLines(wi->dpy, wi->draw_buffer, wi->gc, + xpoints, num_points, CoordModeOrigin); + + if (ISSET(bz->flags, FIRST_END_OK)) { + GetLineEnd(&points[0], &points[1], lw, bz->cap_style, + bz->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(bz->flags, LAST_END_OK)) { + GetLineEnd(&points[num_points-1], &points[num_points-2], lw, + bz->cap_style, bz->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); + } + } + } +} + + +/* + ********************************************************************************** + * + * 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) +{ + BezierItem bz = (BezierItem) item; + WidgetInfo *wi = item->wi; + double dist=1.0e40, new_dist; + ZnPoint *points; + ZnPoint end_points[LINE_END_POINTS]; + int num_points; + int lw = bz->line_width; + + if (bz->dev_points == NULL) { + return dist; + } + +/*printf("Pick in curve\n");*/ + GetBezierPath(bz->dev_points, wi->work_pts); + points = (ZnPoint *) ZnListArray(wi->work_pts); + num_points = ZnListSize(wi->work_pts); + + if (ISSET(bz->flags, FILLED_OK)) { + dist = PolygonToPointDist(points, num_points, p); + if (dist <= 0.0) { + return 0.0; + } + } + + if (lw > 0) { + if (ISCLEAR(bz->flags, RELIEF_OK)) { + new_dist = PolylineToPointDist(points, num_points, lw, + bz->cap_style, JoinRound, p); + if (new_dist < dist) { + dist = new_dist; + } + if (dist <= 0.0) { + return 0.0; + } + } + else { + new_dist = PolygonReliefToPointDist(points, num_points, + ISCLEAR(bz->flags, CCW)?-lw:lw, p); + if (new_dist < dist) { + dist = new_dist; + } + if (dist <= 0.0) { + return 0.0; + } + } + } + + /* + * Check line ends. + */ + if (ISSET(bz->flags, FIRST_END_OK)) { + GetLineEnd(&points[0], &points[1], lw, bz->cap_style, + bz->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(bz->flags, LAST_END_OK)) { + GetLineEnd(&points[num_points-1], &points[num_points-2], lw, + bz->cap_style, bz->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; + } + } + + return dist; +} + + +/* + ********************************************************************************** + * + * PostScript -- + * + ********************************************************************************** + */ +static void +PostScript(Item item, + PostScriptInfo ps_info) +{ +} + + +/* + ********************************************************************************** + * + * GetClipVertices -- + * Get the clipping shape. + * + ********************************************************************************** + */ +static ZnBool +GetClipVertices(Item item, + ZnPoly *poly) +{ + BezierItem bz = (BezierItem) item; + WidgetInfo *wi = item->wi; + + if (bz->dev_points) { + GetBezierPath(bz->dev_points, wi->work_pts); + + POLY_CONTOUR1(poly, (ZnPoint *) ZnListArray(wi->work_pts), + ZnListSize(wi->work_pts)); + } + + 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) +{ + BezierItem bz = (BezierItem) item; + int num_points, i; + ZnPoint *points; + + 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 beziers", NULL); + return ZN_ERROR; + } + if (cmd == COORDS_REPLACE_ALL) { + ZnList tmp; + replace_all: + tmp = ZnListFromArray(*pts, *num_pts, sizeof(ZnPoint)); + if (!bz->points) { + ZnListEmpty(bz->points); + } + else { + bz->points = ZnListNew(*num_pts, sizeof(ZnPoint)); + } + ZnListAppend(bz->points, tmp); + ZnListFree(tmp); + } + else { + if (!bz->points) { + edit_err: + Tcl_AppendResult(item->wi->interp, + " coords command cannot edit empty beziers", NULL); + return ZN_ERROR; + } + points = ZnListArray(bz->points); + num_points = ZnListSize(bz->points); + if (index < 0) { + index += num_points; + } + if ((index < 0) || (index >= num_points)) { + range_err: + Tcl_AppendResult(item->wi->interp, " coord index out of range", NULL); + return ZN_ERROR; + } + points[index] = (*pts)[0]; + } + ITEM.Invalidate(item, ZN_COORDS_FLAG); + } + else if ((cmd == COORDS_READ) || (cmd == COORDS_READ_ALL)) { + if (!bz->points) { + *num_pts = 0; + *pts = NULL; + return ZN_OK; + } + points = ZnListArray(bz->points); + num_points = ZnListSize(bz->points); + if (cmd == COORDS_READ_ALL) { + *num_pts = num_points; + *pts = points; + } + else { + if (index < 0) { + index += num_points; + } + if ((index < 0) || (index >= num_points)) { + goto range_err; + } + *num_pts = 1; + *pts = &points[index]; + } + } + else if ((cmd == COORDS_ADD) || (cmd == COORDS_ADD_LAST)) { + if (!bz->points) { + goto replace_all; + } + else if (cmd == COORDS_ADD) { + num_points = ZnListSize(bz->points); + if (index < 0) { + index += num_points; + } + if ((index < 0) || (index >= num_points)) { + goto range_err; + } + for (i = 0; i < *num_pts; i++, index++) { + ZnListAdd(bz->points, &(*pts)[i], index); + } + } + else { + ZnList tmp; + tmp = ZnListFromArray(*pts, *num_pts, sizeof(ZnPoint)); + ZnListAppend(bz->points, tmp); + ZnListFree(tmp); + } + ITEM.Invalidate(item, ZN_COORDS_FLAG); + } + else if (cmd == COORDS_REMOVE) { + if (!bz->points) { + goto edit_err; + } + points = ZnListArray(bz->points); + num_points = ZnListSize(bz->points); + if (index < 0) { + index += num_points; + } + if ((index < 0) || (index >= num_points)) { + goto range_err; + } + ZnListDelete(bz->points, index); + ITEM.Invalidate(item, ZN_COORDS_FLAG); + } + + return ZN_OK; +} + + +/* + ********************************************************************************** + * + * Exported functions struct -- + * + ********************************************************************************** + */ +static ItemClassStruct BEZIER_ITEM_CLASS = { + sizeof(BezierItemStruct), + False, + False, + False, + "bezier", + bz_attrs, + Init, + Clone, + Destroy, + Configure, + Query, + NULL, + NULL, + GetClipVertices, + Coords, + NULL, /* Contour */ + ComputeCoordinates, + ToArea, + Draw, + IsSensitive, + Pick, + NULL, /* PickVertex */ + PostScript +}; + +ZnItemClassId ZnBezier = (ZnItemClassId) &BEZIER_ITEM_CLASS; |