/* * 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" #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 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 short 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; ZnDim line_width; /* If 0 the path is not drawn, if <2 relief is flat */ ZnImage fill_pattern; ZnGradient *fill_color; ZnImage line_pattern; ZnGradient *line_color; ZnImage tile; /* Private data */ ZnList dev_points; ZnGradient *gradient; ZnTriStrip tristrip; ZnPoint *grad_geo; } 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, "-composealpha", NULL, Tk_Offset(BezierItemStruct, header.flags), COMPOSE_ALPHA_BIT, ZN_DRAW_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, "-fillcolor", NULL, Tk_Offset(BezierItemStruct, fill_color), 0, ZN_COORDS_FLAG|ZN_BORDER_FLAG, False }, { ZN_CONFIG_BITMAP, "-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_LINE_END, "-lastend", NULL, Tk_Offset(BezierItemStruct, last_end), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-linecolor", NULL, Tk_Offset(BezierItemStruct, line_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BITMAP, "-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_TAG_LIST, "-tags", NULL, Tk_Offset(BezierItemStruct, header.tags), 0, 0, False }, { ZN_CONFIG_IMAGE, "-tile", NULL, Tk_Offset(BezierItemStruct, tile), 0, ZN_DRAW_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 } }; /* ********************************************************************************** * * Init -- * ********************************************************************************** */ static int Init(Item item, int *argc, Tcl_Obj *CONST *args[]) { WidgetInfo *wi = item->wi; BezierItem bz = (BezierItem) item; Tcl_Obj **elems; int i, num_elems; ZnPoint p; bz->dev_points = NULL; bz->tristrip.num_strips = 0; bz->gradient = NULL; bz->grad_geo = NULL; /* Init attributes */ SET(item->flags, VISIBLE_BIT); SET(item->flags, SENSITIVE_BIT); SET(item->flags, COMPOSE_ALPHA_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; } if ((Tcl_ListObjGetElements(wi->interp, (*args)[0], &num_elems, &elems) == ZN_ERROR) || ((num_elems % 2) != 0)) { bz_error: 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_GetDoubleFromObj(wi->interp, elems[i], &p.x) == ZN_ERROR) { bz_error2: ZnListFree(bz->points); bz->points = NULL; goto bz_error; } if (Tcl_GetDoubleFromObj(wi->interp, elems[i+1], &p.y) == ZN_ERROR) { goto bz_error2; } ZnListAdd(bz->points, &p, ZnListTail); } (*args)++; (*argc)--; 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 = ZnUnspecifiedImage; bz->fill_pattern = ZnUnspecifiedImage; bz->line_pattern = ZnUnspecifiedImage; bz->cap_style = CapRound; bz->fill_color = ZnGetGradientByValue(wi->fore_color); bz->line_color = ZnGetGradientByValue(wi->fore_color); return ZN_OK; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(Item item) { BezierItem bz = (BezierItem) item; bz->dev_points = NULL; bz->tristrip.num_strips = 0; if (bz->grad_geo) { ZnPoint *grad_geo = ZnMalloc(4*sizeof(ZnPoint)); memcpy(grad_geo, bz->grad_geo, 4*sizeof(ZnPoint)); bz->grad_geo = grad_geo; } if (bz->gradient) { bz->gradient = ZnGetGradientByValue(bz->gradient); } if (bz->points) { bz->points = ZnListDuplicate(bz->points); } if (bz->first_end) { LineEndDuplicate(bz->first_end); } if (bz->last_end) { LineEndDuplicate(bz->last_end); } if (bz->tile != ZnUnspecifiedImage) { bz->tile = ZnGetImageByValue(bz->tile); } if (bz->line_pattern != ZnUnspecifiedImage) { bz->line_pattern = ZnGetImageByValue(bz->line_pattern); } if (bz->fill_pattern != ZnUnspecifiedImage) { bz->fill_pattern = ZnGetImageByValue(bz->fill_pattern); } bz->line_color = ZnGetGradientByValue(bz->line_color); bz->fill_color = ZnGetGradientByValue(bz->fill_color); } /* ********************************************************************************** * * Destroy -- * ********************************************************************************** */ static void Destroy(Item item) { BezierItem bz = (BezierItem) item; if (bz->points) { ZnListFree(bz->points); } if (bz->dev_points) { ZnListFree(bz->dev_points); } if (bz->grad_geo) { ZnFree(bz->grad_geo); } if (bz->tristrip.num_strips) { TRI_FREE(&bz->tristrip); } if (bz->first_end) { LineEndDelete(bz->first_end); } if (bz->last_end) { LineEndDelete(bz->last_end); } if (bz->gradient) { ZnFreeGradient(bz->gradient); } if (bz->tile != ZnUnspecifiedImage) { ZnFreeImage(bz->tile); bz->tile = ZnUnspecifiedImage; } if (bz->line_pattern != ZnUnspecifiedImage) { ZnFreeImage(bz->line_pattern); bz->line_pattern = ZnUnspecifiedImage; } if (bz->fill_pattern != ZnUnspecifiedImage) { ZnFreeImage(bz->fill_pattern); bz->fill_pattern = ZnUnspecifiedImage; } ZnFreeGradient(bz->fill_color); ZnFreeGradient(bz->line_color); } /* ********************************************************************************** * * 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, Tcl_Obj *CONST argv[], int *flags) { WidgetInfo *wi = item->wi; BezierItem bz = (BezierItem) item; int status = ZN_OK; XColor *color; int alpha; status = ZnConfigureAttributes(wi, item, bz_attrs, argc, argv, flags); if (bz->gradient && (ISSET(*flags, ZN_BORDER_FLAG) || (bz->relief == RELIEF_FLAT))) { ZnFreeGradient(bz->gradient); bz->gradient = NULL; } if ((bz->relief != RELIEF_FLAT) && !bz->gradient) { color = ZnGetGradientColor(bz->line_color, 51.0, &alpha); bz->gradient = ZnGetReliefGradient(wi->interp, wi->win, ZnNameOfColor(color), alpha); if (bz->gradient == NULL) { status = ZN_ERROR; } } return status; } /* ********************************************************************************** * * Query -- * ********************************************************************************** */ static int Query(Item item, int argc, Tcl_Obj *CONST argv[]) { if (ZnQueryAttribute(item->wi, item, bz_attrs, 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); if (bz->tristrip.num_strips) { TRI_FREE(&bz->tristrip); } 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. */ ZnGetPolygonReliefBBox(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; if (!ZnGradientFlat(bz->fill_color)) { ZnPoly poly; if (!bz->grad_geo) { bz->grad_geo = ZnMalloc(4*sizeof(ZnPoint)); } if (bz->fill_color->type == ZN_AXIAL_GRADIENT) { POLY_CONTOUR1(&poly, points, num_points); ZnComputeAxialGradient(wi, &poly, bz->fill_color->g.angle, bz->grad_geo); } else if (bz->fill_color->type == ZN_RADIAL_GRADIENT) { POLY_CONTOUR1(&poly, dev_points, num_points); ZnComputeRadialGradient(wi, &poly, &item->item_bounding_box, &bz->fill_color->g.p, bz->grad_geo); } else if (bz->fill_color->type == ZN_PATH_GRADIENT) { ZnTransformPoint(wi->current_transfo, &bz->fill_color->g.p, &bz->grad_geo[0]); } } else { if (bz->grad_geo) { ZnFree(bz->grad_geo); bz->grad_geo = NULL; } } } /* ********************************************************************************** * * ToArea -- * Tell if the object is entirely outside (-1), * entirely inside (1) or in between (0). * ********************************************************************************** */ static int ToArea(Item item, ZnToArea ta) { BezierItem bz = (BezierItem) item; WidgetInfo *wi = item->wi; ZnPoint *points; ZnPoint end_points[LINE_END_POINTS]; int num_points, result=-1, result2; int lw = bz->line_width; ZnBBox *area = ta->area; 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 = ZnPolygonReliefInBBox(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 = ZnListArray(wi->work_pts); num_points = ZnListSize(wi->work_pts); /* * Fill if requested. */ if (ISSET(bz->flags, FILLED_OK)) { values.foreground = ZnPixel(ZnGetGradientColor(bz->fill_color, 0.0, NULL)); gc_mask = GCFillStyle; if (bz->tile != ZnUnspecifiedImage) { /* Fill tiled */ values.fill_style = FillTiled; values.tile = ZnImagePixmap(bz->tile, NULL); 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 != ZnUnspecifiedImage) { /* Fill stippled */ values.fill_style = FillStippled; values.stipple = ZnImagePixmap(bz->fill_pattern, NULL); 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)) { ReliefStyle relief; int relief_dir; relief = bz->relief; relief_dir = relief & RELIEF_MASK; if (ISCLEAR(bz->flags, CCW)) { lw = -lw; if (relief_dir == RELIEF_SUNKEN) { relief_dir = RELIEF_RAISED; } else { relief_dir = RELIEF_SUNKEN; } relief = (relief & ~RELIEF_MASK) | relief_dir; } ZnDrawPolygonRelief(wi, relief, bz->gradient, points, num_points, lw); } else { ZnSetLineStyle(wi, bz->line_style); values.foreground = ZnPixel(ZnGetGradientColor(bz->line_color, 0, NULL)); values.line_width = (lw == 1) ? 0 : lw; values.join_style = JoinRound; values.cap_style = bz->cap_style; if (bz->line_pattern == ZnUnspecifiedImage) { values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCLineWidth|GCJoinStyle|GCCapStyle|GCForeground, &values); } else { values.fill_style = FillStippled; values.stipple = ZnImagePixmap(bz->line_pattern, NULL); 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); } } } } /* ********************************************************************************** * * Render -- * ********************************************************************************** */ #ifdef GLX void BezierRenderCB(void *closure) { #ifdef GPC BezierItem bz = (BezierItem) closure; WidgetInfo *wi = ((Item) closure)->wi; int i, j, num_points; ZnPoint *points; if (bz->tristrip.num_strips == 0) { ZnPoly poly; POLY_CONTOUR1(&poly, (ZnPoint *) ZnListArray(wi->work_pts), ZnListSize(wi->work_pts)); gpc_polygon_to_tristrip((gpc_polygon *) &poly, (gpc_tristrip *) &bz->tristrip); } for (i = 0; i < bz->tristrip.num_strips; i++) { num_points = bz->tristrip.strips[i].num_points; points = bz->tristrip.strips[i].points; glBegin(GL_TRIANGLE_STRIP); for (j = 0; j < num_points; j++, points++) { glVertex2f(points->x, points->y); } glEnd(); } #endif } #endif static void Render(Item item) { #ifdef GLX WidgetInfo *wi = item->wi; BezierItem bz = (BezierItem) item; ZnPoint *points; int num_points; XColor *color; int alpha; if (bz->dev_points == NULL) { return; } GetBezierPath(bz->dev_points, wi->work_pts); points = ZnListArray(wi->work_pts); num_points = ZnListSize(wi->work_pts); /* * Fill if requested. */ if (ISSET(bz->flags, FILLED_OK)) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); if (!ZnGradientFlat(bz->fill_color)) { ZnPoly poly; POLY_CONTOUR1(&poly, points, num_points); ZnRenderGradient(wi, bz->fill_color, BezierRenderCB, bz, bz->grad_geo, &poly); } else if (bz->tile != ZnUnspecifiedImage) { /* Fill tiled */ ZnRenderTile(wi, bz->tile, bz->fill_color, BezierRenderCB, bz, (ZnPoint *) &item->item_bounding_box); } else { if (bz->fill_pattern != ZnUnspecifiedImage) { /* Fill stippled */ /* * Setup polygon stippling. */ glEnable(GL_POLYGON_STIPPLE); glPolygonStipple(ZnImagePattern(bz->fill_pattern, NULL)); } color = ZnGetGradientColor(bz->fill_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); BezierRenderCB(bz); glDisable(GL_POLYGON_STIPPLE); } } /* * Draw the lines between points */ if (bz->line_width) { /* * Drawing with relief disables: ends, line style and line pattern. */ if (ISSET(bz->flags, RELIEF_OK)) { ReliefStyle relief; ZnDim line_width; int relief_dir; relief = bz->relief; line_width = bz->line_width; if (ISCLEAR(bz->flags, CCW)) { line_width = -line_width; relief_dir = relief & RELIEF_MASK; if (relief_dir == RELIEF_SUNKEN) { relief_dir = RELIEF_RAISED; } else { relief_dir = RELIEF_SUNKEN; } relief = (relief & ~RELIEF_MASK) | relief_dir; } ZnRenderPolygonRelief(wi, relief, bz->gradient, True, points, num_points, line_width); } else { ZnLineEnd first = ISSET(bz->flags, FIRST_END_OK) ? bz->first_end : NULL; ZnLineEnd last = ISSET(bz->flags, LAST_END_OK) ? bz->last_end : NULL; ZnRenderPolyline(wi, points, num_points, bz->line_width, bz->line_style, bz->cap_style, JoinRound, first, last, bz->line_color); } } #endif } /* ********************************************************************************** * * 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, ZnPick ps) { BezierItem bz = (BezierItem) item; WidgetInfo *wi = item->wi; double dist=1.0e40, new_dist; ZnPoint *points, *p = ps->point; 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 = ZnPolygonReliefToPointDist(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. * Never ever call TRI_FREE on the tristrip returned by GetClipVertices. * ********************************************************************************** */ static ZnBool GetClipVertices(Item item, ZnTriStrip *tristrip) { #ifdef GPC BezierItem bz = (BezierItem) item; WidgetInfo *wi = item->wi; #else ZnPoint *points; #endif #ifdef GPC tristrip->fan = False; tristrip->num_strips = 0; if (bz->dev_points) { if (bz->tristrip.num_strips == 0) { ZnPoly poly; GetBezierPath(bz->dev_points, wi->work_pts); POLY_CONTOUR1(&poly, (ZnPoint *) ZnListArray(wi->work_pts), ZnListSize(wi->work_pts)); gpc_polygon_to_tristrip((gpc_polygon *) &poly, (gpc_tristrip *) &bz->tristrip); } if (bz->tristrip.num_strips == 1) { TRI_STRIP1(tristrip, bz->tristrip.strips[0].points, bz->tristrip.strips[0].num_points); } else if (bz->tristrip.num_strips > 1) { tristrip->num_strips = bz->tristrip.num_strips; tristrip->strips = bz->tristrip.strips; } } return False; #else ZnListAssertSize(item->wi->work_pts, 2); points = (ZnPoint *) ZnListArray(item->wi->work_pts); TRI_STRIP1(tristrip, points, 2); points[0] = item->item_bounding_box.orig; points[1] = item->item_bounding_box.corner; return True; #endif } /* ********************************************************************************** * * GetContours -- * Get the external contour(s). * Never ever call POLY_FREE on the poly returned by GetContours. * ********************************************************************************** */ static ZnBool GetContours(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; } /* ********************************************************************************** * * 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) { BezierItem bz = (BezierItem) item; int i, k, num_points; ZnPoint *points; ZnReal dist=1.0e40, new_dist, dist2; *contour = *vertex = *o_vertex = -1; if ((bz->line_width > 0) || ISSET(bz->flags, FILLED_OK)) { points = (ZnPoint *) ZnListArray(bz->dev_points); num_points = ZnListSize(bz->dev_points); for (i = 0; i < num_points; i++) { new_dist = hypot(points[i].x - p->x, points[i].y - p->y); if (new_dist < dist) { dist = new_dist; *contour = 0; *vertex = i; } } /* * Update the opposite vertex. */ i = (*vertex+1) % num_points; new_dist = LineToPointDist(&points[*vertex], &points[i], p); k = ((unsigned)(*vertex-1)) % num_points; dist2 = LineToPointDist(&points[*vertex], &points[k], p); if (dist2 < new_dist) { *o_vertex = k; } else { *o_vertex = i; } } } /* ********************************************************************************** * * Exported functions struct -- * ********************************************************************************** */ static ItemClassStruct BEZIER_ITEM_CLASS = { sizeof(BezierItemStruct), 0, /* num_parts */ False, /* has_anchors */ "bezier", bz_attrs, Init, Clone, Destroy, Configure, Query, NULL, NULL, GetClipVertices, GetContours, Coords, NULL, /* InsertChars */ NULL, /* DeleteChars */ NULL, /* Cursor */ NULL, /* Index */ NULL, /* Part */ NULL, /* Selection */ NULL, /* Contour */ ComputeCoordinates, ToArea, Draw, Render, IsSensitive, Pick, PickVertex, /* PickVertex */ PostScript }; ZnItemClassId ZnBezier = (ZnItemClassId) &BEZIER_ITEM_CLASS;