From c565c357ebbcaa5608761b28eb63a15c850f22b4 Mon Sep 17 00:00:00 2001 From: lecoanet Date: Thu, 13 Jan 2000 10:02:58 +0000 Subject: Renommage de l'item de multipoint en curve. Suppression des lignes doubles. Utilisation du module interne de couleur. implantation des fl�ches, du relief des joints, des caps. Ecriture de la m�thode ToArea. --- generic/Curve.c | 1411 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1411 insertions(+) create mode 100644 generic/Curve.c (limited to 'generic/Curve.c') diff --git a/generic/Curve.c b/generic/Curve.c new file mode 100644 index 0000000..f1a836b --- /dev/null +++ b/generic/Curve.c @@ -0,0 +1,1411 @@ +/* + * 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; + RadarList first_end_points; + RadarList last_end_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->first_end_points = NULL; + cv->last_end_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->first_end_points = NULL; + cv->last_end_points = NULL; + 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->first_end_points) { + RadarListFree(cv->first_end_points); + } + if (cv->last_end_points) { + RadarListFree(cv->last_end_points); + } + 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++) { + /* Compute device coordinates */ + 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; + 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); + printf("filled = %d\n", result); + if (result == 0) { + return 0; + } + } + else { + result = -1; + } + printf("result1 = %d\n", result); + if (cv->line_width > 0) { + if (ISCLEAR(cv->flags, RELIEF_OK)) { + if (PolylineInBBox(points, num_points, cv->line_width, + cv->cap_style, cv->join_style, area) != result) { + printf("overlap plain line\n"); + return 0; + } + } + else { + if (PolygonReliefInBBox(actual_points, cv->line_width, area) != result) { + printf("overlap relief\n"); + return 0; + } + } + } + printf("result2 = %d\n", result); + + /* + * 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; -- cgit v1.1