/* * 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" #include #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 SMOOTHED_BIT 1<<2 /* If we `smooth' the path with Bezier curves */ #define BEZIER_BIT 1<<3 /* If we interpret the vertices as Bezier controls. */ #define CCW 1<<5 /* Tell if the vertices are described in * clockwise or ccw order. */ #define FIRST_END_OK 1<<6 #define LAST_END_OK 1<<7 #define FILLED_OK 1<<8 #define SMOOTHED_OK 1<<9 #define RELIEF_OK 1<<10 #define BEZIER_OK 1<<11 #define MARKER_OK 1<<12 /* ********************************************************************************** * * Specific Curve item record * ********************************************************************************** */ typedef struct _CurveItemStruct { ItemStruct header; /* Public data */ RadarList points; unsigned int flags; Pixmap marker; LineEnd first_end; /* These two are considered only if relief is flat */ LineEnd 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; RadarColor fill_color; Pixmap line_pattern; RadarColor line_color; RadarColor marker_color; char *tile_name; /* Private data */ RadarImage tile; RadarList dev_points; RadarColorGradient gradient; } CurveItemStruct, *CurveItem; /* * Need RADAR_COORDS_FLAG in -smoothed and -bezier because * -relief may need a reversing of vertices and this is done * lazily. */ static RadarAttrConfig cv_attrs[] = { { RADAR_CONFIG_BOOL, "-bezier", NULL, Tk_Offset(CurveItemStruct, flags), BEZIER_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_CAP_STYLE, "-capstyle", NULL, Tk_Offset(CurveItemStruct, cap_style), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_BOOL, "-composerotation", NULL, Tk_Offset(CurveItemStruct, header.flags), COMPOSE_ROTATION_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_BOOL, "-composescale", NULL, Tk_Offset(CurveItemStruct, header.flags), COMPOSE_SCALE_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_COLOR, "-fillcolor", NULL, Tk_Offset(CurveItemStruct, fill_color), 0, RADAR_DRAW_FLAG|RADAR_BORDER_FLAG, False }, { RADAR_CONFIG_PATTERN, "-fillpattern", NULL, Tk_Offset(CurveItemStruct, fill_pattern), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_BOOL, "-filled", NULL, Tk_Offset(CurveItemStruct, flags), FILLED_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_LINE_END, "-firstend", NULL, Tk_Offset(CurveItemStruct, first_end), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_JOIN_STYLE, "-joinstyle", NULL, Tk_Offset(CurveItemStruct, join_style), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_LINE_END, "-lastend", NULL, Tk_Offset(CurveItemStruct, last_end), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_COLOR, "-linecolor", NULL, Tk_Offset(CurveItemStruct, line_color), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_PATTERN, "-linepattern", NULL, Tk_Offset(CurveItemStruct, line_pattern), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_LINE_STYLE, "-linestyle", NULL, Tk_Offset(CurveItemStruct, line_style), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_DIM, "-linewidth", NULL, Tk_Offset(CurveItemStruct, line_width), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_PRI, "-priority", NULL, Tk_Offset(CurveItemStruct, header.priority), 0, RADAR_DRAW_FLAG|RADAR_REPICK_FLAG, False }, { RADAR_CONFIG_PATTERN, "-marker", NULL, Tk_Offset(CurveItemStruct, marker), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_COLOR, "-markercolor", NULL, Tk_Offset(CurveItemStruct, marker_color), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_RELIEF, "-relief", NULL, Tk_Offset(CurveItemStruct, relief), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(CurveItemStruct, header.flags), SENSITIVE_BIT, RADAR_REPICK_FLAG, False }, { RADAR_CONFIG_BOOL, "-smoothed", NULL, Tk_Offset(CurveItemStruct, flags), SMOOTHED_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_TAGS, "-tags", NULL, Tk_Offset(CurveItemStruct, header.tags), 0, 0, False }, { RADAR_CONFIG_IMAGE, "-tile", NULL, Tk_Offset(CurveItemStruct, tile_name), 0, RADAR_DRAW_FLAG|RADAR_TILE_FLAG, False }, { RADAR_CONFIG_BOOL, "-visible", NULL, Tk_Offset(CurveItemStruct, header.flags), VISIBLE_BIT, RADAR_DRAW_FLAG|RADAR_REPICK_FLAG|RADAR_VIS_FLAG, False }, { RADAR_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); ITEM.Invalidate((Item) cv, RADAR_COORDS_FLAG); } /* ********************************************************************************** * * Init -- * ********************************************************************************** */ static int Init(Item item, int *argc, Arg **args) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; Arg *elems; int i, result, num_elems; RadarPoint p; #ifdef PTK LangFreeProc *freeProc = NULL; #endif cv->dev_points = NULL; 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); item->priority = DEFAULT_MULTI_POINT_PRIORITY; if (*argc < 1) { Tcl_AppendResult(wi->interp, " curve coords expected", NULL); return RADAR_ERROR; } result = Lang_SplitList(wi->interp, (*args)[0], &num_elems, &elems, &freeProc); if ((result == RADAR_ERROR) || ((num_elems%2) != 0)) { cv_error: #ifdef PTK if (elems != NULL && freeProc) { (*freeProc)(num_elems, elems); } #endif Tcl_AppendResult(wi->interp, " malformed curve coords", NULL); return RADAR_ERROR; } cv->points = RadarListNew(num_elems/2, sizeof(RadarPoint)); for (i = 0; i < num_elems; i += 2) { if (Tcl_GetDouble(wi->interp, elems[i], &p.x) == RADAR_ERROR) { cv_error2: #ifndef PTK Tcl_Free((char *) elems); #endif RadarListFree(cv->points); cv->points = NULL; goto cv_error; } if (Tcl_GetDouble(wi->interp, elems[i+1], &p.y) == RADAR_ERROR) { goto cv_error2; } RadarListAdd(cv->points, &p, RadarListTail); } (*args)++; (*argc)--; #ifndef PTK Tcl_Free((char *) elems); #else if (freeProc) { (*freeProc)(num_elems, elems); } #endif CLEAR(cv->flags, FILLED_BIT); CLEAR(cv->flags, SMOOTHED_BIT); CLEAR(cv->flags, BEZIER_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 = RadarUnspecifiedImage; cv->fill_pattern = RadarUnspecifiedPattern; cv->line_pattern = RadarUnspecifiedPattern; 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 = RadarUnspecifiedPattern; cv->fill_color = RadarGetColorByValue(wi->win, wi->fore_color); cv->line_color = RadarGetColorByValue(wi->win, wi->fore_color); cv->marker_color = RadarGetColorByValue(wi->win, wi->fore_color); return RADAR_OK; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(Item item) { CurveItem cv = (CurveItem) item; WidgetInfo *wi = item->wi; char *text; cv->dev_points = NULL; if (cv->gradient) { cv->gradient = RadarGetColorGradientByValue(cv->gradient); } if (cv->points) { cv->points = RadarListDuplicate(cv->points); } if (cv->line_pattern != RadarUnspecifiedPattern) { 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 = RadarMalloc((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 != RadarUnspecifiedPattern) { cv->fill_pattern = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, cv->fill_pattern)); } if (cv->marker != RadarUnspecifiedPattern) { cv->marker = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, cv->marker)); } cv->line_color = RadarGetColorByValue(wi->win, cv->line_color); cv->fill_color = RadarGetColorByValue(wi->win, cv->fill_color); cv->marker_color = RadarGetColorByValue(wi->win, cv->marker_color); } /* ********************************************************************************** * * Destroy -- * ********************************************************************************** */ static void Destroy(Item item) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; if (cv->points) { RadarListFree(cv->points); } if (cv->dev_points) { RadarListFree(cv->dev_points); } if (cv->first_end) { LineEndDelete(cv->first_end); } if (cv->last_end) { LineEndDelete(cv->last_end); } if (cv->gradient) { RadarFreeColorGradient(cv->gradient); } if (strlen(cv->tile_name) != 0) { RadarFree(cv->tile_name); } if (cv->tile != RadarUnspecifiedImage) { Tk_FreeImage(cv->tile); cv->tile = RadarUnspecifiedImage; } if (cv->line_pattern != RadarUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, cv->line_pattern); } if (cv->fill_pattern != RadarUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, cv->fill_pattern); } if (cv->marker != RadarUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, cv->marker); } RadarFreeColor(cv->fill_color); RadarFreeColor(cv->line_color); RadarFreeColor(cv->marker_color); } /* ********************************************************************************** * * 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) && (RadarListSize(cv->points) > 2)); ASSIGN(cv->flags, BEZIER_OK, ISSET(cv->flags, BEZIER_BIT) && (RadarListSize(cv->points) > 2) && (ISSET(cv->flags, FILLED_BIT) || cv->line_width)); ASSIGN(cv->flags, SMOOTHED_OK, ISSET(cv->flags, SMOOTHED_BIT) && (RadarListSize(cv->points) > 2) && ISCLEAR(cv->flags, BEZIER_OK) && (ISSET(cv->flags, FILLED_BIT) || cv->line_width)); ASSIGN(cv->flags, RELIEF_OK, (cv->relief != RELIEF_FLAT) && (RadarListSize(cv->points) > 1) && (cv->line_width > 1) && ISCLEAR(cv->flags, SMOOTHED_OK) && ISCLEAR(cv->flags, BEZIER_OK)); ASSIGN(cv->flags, MARKER_OK, (cv->marker != RadarUnspecifiedPattern) && ISCLEAR(cv->flags, BEZIER_OK) && ISCLEAR(cv->flags, SMOOTHED_OK) && ISCLEAR(cv->flags, RELIEF_OK)); ASSIGN(cv->flags, FIRST_END_OK, (cv->first_end != NULL) && (RadarListSize(cv->points) > 1) && ISCLEAR(cv->flags, FILLED_BIT) && cv->line_width && ISCLEAR(cv->flags, RELIEF_OK)); ASSIGN(cv->flags, LAST_END_OK, (cv->last_end != NULL) && (RadarListSize(cv->points) > 1) && ISCLEAR(cv->flags, FILLED_BIT) && cv->line_width && ISCLEAR(cv->flags, RELIEF_OK)); } /* ********************************************************************************** * * Configure -- * ********************************************************************************** */ static int Configure(Item item, int argc, RadarAttrList argv, int *flags) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; if (ITEM_P.ConfigureAttributes((char *) item, -1, argc, argv, flags) == RADAR_ERROR) { return RADAR_ERROR; } if (cv->gradient && (ISSET(*flags, RADAR_BORDER_FLAG) || (cv->relief == RELIEF_FLAT))) { RadarFreeColorGradient(cv->gradient); cv->gradient = NULL; } if ((cv->relief != RELIEF_FLAT) && !cv->gradient) { cv->gradient = RadarGetReliefGradient(wi->interp, wi->win, RadarNameOfColor(cv->fill_color)); } if (ISSET(*flags, RADAR_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. */ return RADAR_ERROR; } } else { tile = RadarUnspecifiedImage; } if (cv->tile != RadarUnspecifiedImage) { Tk_FreeImage(cv->tile); } cv->tile = tile; } SetRenderFlags(cv); return RADAR_OK; } /* ********************************************************************************** * * Query -- * ********************************************************************************** */ static int Query(Item item, int argc, RadarAttrList argv) { if (ITEM_P.QueryAttribute((char *) item, -1, argv[0]) == RADAR_ERROR) { return RADAR_ERROR; } return RADAR_OK; } static RadarBool TestCCW(RadarPoint *points, int num_points) { RadarPoint *p, *p_p, *p_n, min; RadarReal 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, RadarBool force) { WidgetInfo *wi = item->wi; CurveItem cv = (CurveItem) item; int i, j; RadarPoint *points; RadarPoint end_points[LINE_END_POINTS]; RadarPoint *dev_points, *p; int num_points; RadarBBox bbox; int lw; ResetBBox(&item->item_bounding_box); if (cv->points == NULL) { return; } points = (RadarPoint *) RadarListArray(cv->points); num_points = RadarListSize(cv->points); /* * Allocate space for devices coordinates */ if (cv->dev_points == NULL) { cv->dev_points = RadarListNew(num_points, sizeof(RadarPoint)); } RadarListAssertSize(cv->dev_points, num_points); dev_points = (RadarPoint *) RadarListArray(cv->dev_points); /* * Process all points, skipping adjacent points that transform * to the same device location. */ for (i = 0, j = 0; i < num_points; i++, j++) { /* Compute device coordinates. * Identical vertices are no longer skipped to allow a better control * on Bezier curves and smoothing. */ RadarTransformPoint(wi->current_transfo, &points[i], &dev_points[j]); /* if ((j == 0) || (dev_points[j].x != dev_points[j-1].x) || (dev_points[j].y != dev_points[j-1].y)) { j++; } */ } /* * Adjust the device coords list to the actual size. */ RadarListTruncate(cv->dev_points, j); /*printf("==========num_points %d=============\n", RadarListSize(cv->dev_points));*/ lw = cv->line_width; ASSIGN(cv->flags, CCW, TestCCW(dev_points, num_points)); if (ISCLEAR(cv->flags, CCW)) { if (ISSET(cv->flags, RELIEF_OK)) { RadarPoint tmp; int mid = num_points/2; /* * Revert the points to draw the relief inside. */ for (i = 0; i < mid; i++) { tmp = dev_points[i]; dev_points[i] = dev_points[num_points-i-1]; dev_points[num_points-i-1] = tmp; } } } /* * Add to bounding box. */ if (ISSET(cv->flags, RELIEF_OK)) { GetPolygonReliefBBox(cv->dev_points, lw, &bbox); AddBBoxToBBox(&item->item_bounding_box, &bbox); return; } for (i = 0; i < j; i++) { AddPointToBBox(&item->item_bounding_box, dev_points[i].x, dev_points[i].y); } /* * 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; /* * Take care of miters. */ if (cv->join_style == JoinMiter) { RadarPoint miter_i, miter_o; for (i = j, p = dev_points; i >= 3; i--, p++) { GetMiterPoints(p, p+1, p+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; RadarBBox bbox; Tk_SizeOfBitmap(wi->dpy, cv->marker, &w, &h); w = w/2 + 2; h = w/2 + 2; for (i = 0; i < j; i++) { bbox.orig.x = dev_points[i].x - w; bbox.orig.y = dev_points[i].y - h; bbox.corner.x = dev_points[i].x + w; bbox.corner.y = dev_points[i].y + h; AddBBoxToBBox(&item->item_bounding_box, &bbox); } } /* * Process arrows. */ if (ISSET(cv->flags, FIRST_END_OK)) { GetLineEnd(&dev_points[0], &dev_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(&dev_points[j-1], &dev_points[j-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, RadarBBox *area, Tk_Uid tag_uid, int enclosed, RadarBool report) { CurveItem cv = (CurveItem) item; WidgetInfo *wi = item->wi; RadarBBox bbox; RadarPoint *points; RadarPoint end_points[LINE_END_POINTS]; int num_points, result, result2; RadarList actual_points; if (cv->dev_points == NULL) { return -1; } printf("============== poly %d ==============\n", item->id); if (ISSET(cv->flags, SMOOTHED_OK)) { SmoothPathWithBezier(cv->dev_points, wi->work_pts); actual_points = wi->work_pts; } else if (ISSET(cv->flags, BEZIER_OK)) { GetBezierPath(cv->dev_points, wi->work_pts); actual_points = wi->work_pts; } else { actual_points = cv->dev_points; } points = (RadarPoint *) RadarListArray(actual_points); num_points = RadarListSize(actual_points); if (ISSET(cv->flags, FILLED_OK)) { result = PolygonInBBox(points, num_points, area); if (result == 0) { return 0; } } if (cv->line_width > 0) { if (ISCLEAR(cv->flags, RELIEF_OK)) { result2 = PolylineInBBox(points, num_points, cv->line_width, cv->cap_style, cv->join_style, area); } else { result2 = PolygonReliefInBBox(actual_points, cv->line_width, area); } if (ISCLEAR(cv->flags, FILLED_OK)) { if (result2 == 0) { return 0; } result = result2; } else if (result2 != result) { return 0; } /* * 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); if (PolygonInBBox(end_points, LINE_END_POINTS, area) != 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) != result) { return 0; } } /* * Last, check markers */ if (ISSET(cv->flags, MARKER_OK)) { int num_points, width, height; RadarPoint *points; points = (RadarPoint *) RadarListArray(actual_points); num_points = RadarListSize(actual_points); if (ISSET(cv->flags, FIRST_END_OK)) { num_points--; points++; } if (ISSET(cv->flags, LAST_END_OK) || ((points[0].x == points[num_points-1].x) && (points[0].y == points[num_points-1].y))) { 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, num_points; unsigned int gc_mask; RadarPoint *points; XPoint *xpoints = NULL; if (cv->dev_points == NULL) { return; } if (ISSET(cv->flags, SMOOTHED_OK)) { SmoothPathWithBezier(cv->dev_points, wi->work_pts); points = (RadarPoint *) RadarListArray(wi->work_pts); num_points = RadarListSize(wi->work_pts); } else if (ISSET(cv->flags, BEZIER_OK)) { GetBezierPath(cv->dev_points, wi->work_pts); points = (RadarPoint *) RadarListArray(wi->work_pts); num_points = RadarListSize(wi->work_pts); } else { points = (RadarPoint *) RadarListArray(cv->dev_points); num_points = RadarListSize(cv->dev_points); } /* * Fill if requested. */ if (ISSET(cv->flags, FILLED_OK)) { values.foreground = RadarPixel(cv->fill_color); gc_mask = GCFillStyle; if (cv->tile != RadarUnspecifiedImage) { /* Fill tiled */ Pixmap pmap = GetImagePixmap(wi->win, cv->tile); 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 != RadarUnspecifiedPattern) { /* 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); if (!xpoints) { xpoints = (XPoint *) alloca(num_points*sizeof(XPoint)); 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 (cv->line_width) { RadarPoint 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)) { DrawPolygonRelief(wi, cv->relief, cv->gradient, points, num_points, cv->line_width); } else { SetLineStyle(wi->dpy, wi->gc, cv->line_style); values.foreground = RadarPixel(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 == RadarUnspecifiedPattern) { 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); } if (!xpoints) { xpoints = alloca(num_points*sizeof(XPoint)); 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(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. Smoothed line or relief disable this. * If ends are specified or if last point join first point suppress * markers as needed. */ if (ISSET(cv->flags, MARKER_OK)) { int width, h_width, height, h_height; RadarPoint ptmp, *p; int i; i = num_points; p = points; if (ISSET(cv->flags, FIRST_END_OK)) { i--; p++; } if (ISSET(cv->flags, LAST_END_OK) || ((p[0].x == p[num_points-1].x) && (p[0].y == p[num_points-1].y))) { i--; } values.foreground = RadarPixel(cv->marker_color); 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; for (; i > 0; i--, p++) { ptmp.x = p->x - h_width; ptmp.y = p->y - h_height; values.ts_x_origin = ptmp.x; values.ts_y_origin = ptmp.y; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCStipple|GCTileStipXOrigin|GCTileStipYOrigin|GCForeground, &values); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, ptmp.x, ptmp.y, width, height); } } /* * If nothing useful is requested then why have you * created this item ? Try to do something useful, we * guess that drawing points is useful. */ if (ISCLEAR(cv->flags, FILLED_OK) && !cv->line_width && (cv->marker == RadarUnspecifiedPattern)) { values.foreground = RadarPixel(cv->marker_color); values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCForeground | GCFillStyle, &values); if (!xpoints) { xpoints = alloca(num_points*sizeof(XPoint)); for (i = 0; i < num_points; i++) { xpoints[i].x = points[i].x; xpoints[i].y = points[i].y; } } XDrawPoints(wi->dpy, wi->draw_buffer, wi->gc, xpoints, num_points, CoordModeOrigin); } } /* ********************************************************************************** * * IsSensitive -- * ********************************************************************************** */ static RadarBool IsSensitive(Item item, int item_part) { return (ISSET(item->flags, SENSITIVE_BIT) && item->parent->class->IsSensitive(item->parent, RADAR_NO_PART)); } /* ********************************************************************************** * * Pick -- * ********************************************************************************** */ static double Pick(Item item, RadarPoint *p, Item start_item, int aperture, Item *a_item, int *part) { CurveItem cv = (CurveItem) item; WidgetInfo *wi = item->wi; RadarBBox bbox; double dist=1.0e40, new_dist; RadarPoint *points; RadarPoint end_points[LINE_END_POINTS]; int num_points; RadarList actual_points; if (cv->dev_points == NULL) { return dist; } /*printf("Pick in curve\n");*/ if (ISSET(cv->flags, SMOOTHED_OK)) { SmoothPathWithBezier(cv->dev_points, wi->work_pts); actual_points = wi->work_pts; } else if (ISSET(cv->flags, BEZIER_OK)) { GetBezierPath(cv->dev_points, wi->work_pts); actual_points = wi->work_pts; } else { actual_points = cv->dev_points; } points = (RadarPoint *) RadarListArray(actual_points); num_points = RadarListSize(actual_points); if (ISSET(cv->flags, FILLED_OK)) { dist = PolygonToPointDist(points, num_points, p); if (dist <= 0.0) { return 0.0; } } if (cv->line_width > 0) { if (ISCLEAR(cv->flags, RELIEF_OK)) { new_dist = PolylineToPointDist(points, 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(actual_points, cv->line_width, p); if (new_dist < dist) { dist = new_dist; } if (dist <= 0.0) { return 0.0; } } } /* * 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 */ if (ISSET(cv->flags, MARKER_OK)) { int num_points, width, height; RadarPoint *points; points = (RadarPoint *) RadarListArray(actual_points); num_points = RadarListSize(actual_points); if (ISSET(cv->flags, FIRST_END_OK)) { num_points--; points++; } if (ISSET(cv->flags, LAST_END_OK) || ((points[0].x == points[num_points-1].x) && (points[0].y == points[num_points-1].y))) { 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 RadarBool GetClipVertices(Item item, RadarPoint **points, int *num_points) { CurveItem cv = (CurveItem) item; *points = NULL; *num_points = 0; if (cv->dev_points) { *points = (RadarPoint *) RadarListArray(cv->dev_points); *num_points = RadarListSize(cv->dev_points); } return False; } /* ********************************************************************************** * * Coords -- * Return or edit the item vertices. * ********************************************************************************** */ static int Coords(Item item, int index, int cmd, RadarPoint **pts, int *num_pts) { CurveItem cv = (CurveItem) item; int num_points, i; RadarPoint *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 curves", NULL); return RADAR_ERROR; } if (cmd == COORDS_REPLACE_ALL) { RadarList tmp; replace_all: tmp = RadarListFromArray(*pts, *num_pts, sizeof(RadarPoint)); if (!cv->points) { RadarListEmpty(cv->points); } else { cv->points = RadarListNew(*num_pts, sizeof(RadarPoint)); } RadarListAppend(cv->points, tmp); RadarListFree(tmp); } else { if (!cv->points) { edit_err: Tcl_AppendResult(item->wi->interp, " coords command cannot edit empty curves", NULL); return RADAR_ERROR; } points = RadarListArray(cv->points); num_points = RadarListSize(cv->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 RADAR_ERROR; } points[index] = (*pts)[0]; } ITEM.Invalidate(item, RADAR_COORDS_FLAG); } else if ((cmd == COORDS_READ) || (cmd == COORDS_READ_ALL)) { if (!cv->points) { *num_pts = 0; *pts = NULL; return RADAR_OK; } points = RadarListArray(cv->points); num_points = RadarListSize(cv->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 (!cv->points) { goto replace_all; } else if (cmd == COORDS_ADD) { num_points = RadarListSize(cv->points); if (index < 0) { index += num_points; } if ((index < 0) || (index >= num_points)) { goto range_err; } for (i = 0; i < *num_pts; i++, index++) { RadarListAdd(cv->points, &(*pts)[i], index); } } else { RadarList tmp; tmp = RadarListFromArray(*pts, *num_pts, sizeof(RadarPoint)); RadarListAppend(cv->points, tmp); RadarListFree(tmp); } ITEM.Invalidate(item, RADAR_COORDS_FLAG); } else if (cmd == COORDS_REMOVE) { if (!cv->points) { goto edit_err; } points = RadarListArray(cv->points); num_points = RadarListSize(cv->points); if (index < 0) { index += num_points; } if ((index < 0) || (index >= num_points)) { goto range_err; } RadarListDelete(cv->points, index); ITEM.Invalidate(item, RADAR_COORDS_FLAG); } return RADAR_OK; } /* ********************************************************************************** * * Exported functions struct -- * ********************************************************************************** */ static ItemClassStruct CURVE_ITEM_CLASS = { sizeof(CurveItemStruct), False, False, False, "curve", cv_attrs, Init, Clone, Destroy, Configure, Query, NULL, NULL, GetClipVertices, Coords, ComputeCoordinates, ToArea, Draw, IsSensitive, Pick, PostScript }; RadarItemClassId RadarCurve = (RadarItemClassId) &CURVE_ITEM_CLASS;