aboutsummaryrefslogtreecommitdiff
path: root/Bezier.c
diff options
context:
space:
mode:
authorlecoanet2000-05-11 13:47:22 +0000
committerlecoanet2000-05-11 13:47:22 +0000
commitf0ae145aeb0ab1b6d02182fdc7c79349951eae45 (patch)
treeee95768bea94777b300a473e3105873c5099df4a /Bezier.c
parenta79c07edd65c728cd76d067bf751e79ab95b15dd (diff)
downloadtkzinc-f0ae145aeb0ab1b6d02182fdc7c79349951eae45.zip
tkzinc-f0ae145aeb0ab1b6d02182fdc7c79349951eae45.tar.gz
tkzinc-f0ae145aeb0ab1b6d02182fdc7c79349951eae45.tar.bz2
tkzinc-f0ae145aeb0ab1b6d02182fdc7c79349951eae45.tar.xz
Partie Bezier de curve
Diffstat (limited to 'Bezier.c')
-rw-r--r--Bezier.c1138
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;