/* * Arc.c -- Implementation of Arc item. * * Authors : Patrick Lecoanet. * Creation date : Wed Mar 30 16:24:09 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 #include "Item.h" #include "Geo.h" #include "Draw.h" #include "Types.h" #include "WidgetInfo.h" #include "Image.h" static const char rcsid[] = "$Id$"; static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; /* * Bit offset of flags. */ #define FILLED_BIT 1 /* If the arc is filled with color/pattern */ #define CLOSED_BIT 2 /* If the arc outline is closed */ #define PIE_SLICE_BIT 4 /* If the arc is closed as a pie slice or a chord */ #define PTS_IN_SHAPE 64 static RadarPoint oval_shape[PTS_IN_SHAPE] = { {1.0, 0.0}, /* 0 */ {0.99518472653, 0.0980171417729}, {0.980785279837, 0.195090324861}, {0.956940334469, 0.290284681418}, {0.923879530291, 0.382683437725}, {0.88192126093, 0.471396743221}, {0.831469607468, 0.555570240255}, {0.773010446922, 0.634393292011}, {0.707106772982, 0.707106789391}, {0.634393274074, 0.773010461643}, {0.555570220961, 0.83146962036}, {0.471396722756, 0.881921271869}, {0.382683416286, 0.923879539171}, {0.290284659212, 0.956940341205}, {0.195090302102, 0.980785284364}, {0.0980171186795, 0.995184728805}, {0.0, 1.0}, /* PI/2 */ {-0.0980171648663, 0.995184724256}, {-0.19509034762, 0.98078527531}, {-0.290284703624, 0.956940327733}, {-0.382683459163, 0.923879521411}, {-0.471396763686, 0.881921249991}, {-0.555570259549, 0.831469594576}, {-0.634393309949, 0.773010432201}, {-0.707106805799, 0.707106756574}, {-0.773010476365, 0.634393256136}, {-0.831469633252, 0.555570201666}, {-0.881921282808, 0.471396702291}, {-0.923879548052, 0.382683394847}, {-0.956940347941, 0.290284637006}, {-0.980785288892, 0.195090279343}, {-0.995184731079, 0.0980170955862}, {-1.0, 0.0}, /* PI */ {-0.995184721981, -0.0980171879596}, {-0.980785270783, -0.195090370379}, {-0.956940320997, -0.29028472583}, {-0.923879512531, -0.382683480602}, {-0.881921239052, -0.471396784151}, {-0.831469581684, -0.555570278844}, {-0.77301041748, -0.634393327887}, {-0.707106740165, -0.707106822208}, {-0.634393238198, -0.773010491086}, {-0.555570182372, -0.831469646144}, {-0.471396681826, -0.881921293746}, {-0.382683373409, -0.923879556932}, {-0.2902846148, -0.956940354677}, {-0.195090256583, -0.980785293419}, {-0.0980170724928, -0.995184733354}, {0.0, -1.0}, /* 3*PI/2 */ {0.098017211053, -0.995184719707}, {0.195090393139, -0.980785266256}, {0.290284748036, -0.956940314261}, {0.382683502041, -0.923879503651}, {0.471396804617, -0.881921228114}, {0.555570298138, -0.831469568792}, {0.634393345825, -0.773010402759}, {0.707106838616, -0.707106723757}, {0.773010505807, -0.63439322026}, {0.831469659036, -0.555570163078}, {0.881921304685, -0.471396661361}, {0.923879565812, -0.38268335197}, {0.956940361414, -0.290284592594}, {0.980785297946, -0.195090233824}, {0.995184735628, -0.0980170493994} }; /* ********************************************************************************** * * Specific Arc item record. * ********************************************************************************** */ typedef struct _ArcItemStruct { ItemStruct header; /* Public data */ RadarPoint coords[2]; int start_angle; int angle_extent; Pixmap line_pattern; Pixmap fill_pattern; RadarColor fill_color; RadarColor line_color; int line_width; LineStyle line_style; char *tile_name; unsigned char flags; /* Private data */ RadarPoint orig; RadarPoint corner; RadarPoint center1; RadarPoint center2; RadarImage tile; RadarList render_shape; } ArcItemStruct, *ArcItem; static RadarAttrConfig arc_attrs[] = { { RADAR_CONFIG_BOOL, "-closed", NULL, Tk_Offset(ArcItemStruct, flags), CLOSED_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_BOOL, "-composerotation", NULL, Tk_Offset(ArcItemStruct, header.flags), COMPOSE_ROTATION_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_BOOL, "-composescale", NULL, Tk_Offset(ArcItemStruct, header.flags), COMPOSE_SCALE_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_ANGLE, "-extent", NULL, Tk_Offset(ArcItemStruct, angle_extent), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_COLOR, "-fillcolor", NULL, Tk_Offset(ArcItemStruct, fill_color), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_BOOL, "-filled", NULL, Tk_Offset(ArcItemStruct, flags), FILLED_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_PATTERN, "-fillpattern", NULL, Tk_Offset(ArcItemStruct, fill_pattern), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_COLOR, "-linecolor", NULL, Tk_Offset(ArcItemStruct, line_color), 0, RADAR_DRAW_FLAG|RADAR_BORDER_FLAG, False }, { RADAR_CONFIG_PATTERN, "-linepattern", NULL, Tk_Offset(ArcItemStruct, line_pattern), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_LINE_STYLE, "-linestyle", NULL, Tk_Offset(ArcItemStruct, line_style), 0, RADAR_DRAW_FLAG, False }, { RADAR_CONFIG_DIM, "-linewidth", NULL, Tk_Offset(ArcItemStruct, line_width), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_BOOL, "-pieslice", NULL, Tk_Offset(ArcItemStruct, flags), PIE_SLICE_BIT, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_PRI, "-priority", NULL, Tk_Offset(ArcItemStruct, header.priority), 0, RADAR_DRAW_FLAG|RADAR_REPICK_FLAG, False }, { RADAR_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(ArcItemStruct, header.flags), SENSITIVE_BIT, RADAR_REPICK_FLAG, False }, { RADAR_CONFIG_ANGLE, "-startangle", NULL, Tk_Offset(ArcItemStruct, start_angle), 0, RADAR_COORDS_FLAG, False }, { RADAR_CONFIG_TAGS, "-tags", NULL, Tk_Offset(ArcItemStruct, header.tags), 0, 0, False }, { RADAR_CONFIG_IMAGE, "-tile", NULL, Tk_Offset(ArcItemStruct, tile_name), 0, RADAR_DRAW_FLAG|RADAR_TILE_FLAG, False }, { RADAR_CONFIG_BOOL, "-visible", NULL, Tk_Offset(ArcItemStruct, header.flags), VISIBLE_BIT, RADAR_DRAW_FLAG|RADAR_REPICK_FLAG|RADAR_VIS_FLAG, False }, { RADAR_CONFIG_END, NULL, NULL, 0, 0, 0 } }; /* ********************************************************************************** * * ArcTileChange -- * ********************************************************************************** */ static void ArcTileChange(ClientData client_data, int x, int y, int width, int height, int image_width, int image_height) { ArcItem arc = (ArcItem) client_data; InvalidateImage(arc->tile); ITEM.Invalidate((Item) arc, RADAR_COORDS_FLAG); } /* ********************************************************************************** * * Init -- * ********************************************************************************** */ static int Init(Item item, int *argc, Arg **args) { WidgetInfo *wi = item->wi; ArcItem arc = (ArcItem) item; Arg *elems; int result, num_elems; #ifdef PTK LangFreeProc *freeProc = NULL; #endif /* 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_ARC_PRIORITY; arc->start_angle = 0; arc->angle_extent = 360; CLEAR(arc->flags, FILLED_BIT); CLEAR(arc->flags, CLOSED_BIT); CLEAR(arc->flags, PIE_SLICE_BIT); arc->line_pattern = RadarUnspecifiedPattern; arc->fill_pattern = RadarUnspecifiedPattern; arc->line_style = LINE_SIMPLE; arc->line_width = 1; arc->tile_name = ""; arc->tile = RadarUnspecifiedImage; arc->render_shape = NULL; if (*argc < 1) { Tcl_AppendResult(wi->interp, " arc coords expected", NULL); return RADAR_ERROR; } result = Lang_SplitList(wi->interp, (*args)[0], &num_elems, &elems, &freeProc); if ((result == RADAR_ERROR) || (num_elems != 4)) { arc_error: #ifdef PTK if (elems != NULL && freeProc) { (*freeProc)(num_elems, elems); } #endif Tcl_AppendResult(wi->interp, " malformed arc coords", NULL); return RADAR_ERROR; } if (Tcl_GetDouble(wi->interp, elems[0], &arc->coords[0].x) == RADAR_ERROR) { arc_error2: #ifndef PTK Tcl_Free((char *) elems); #endif goto arc_error; }; if (Tcl_GetDouble(wi->interp, elems[1], &arc->coords[0].y) == RADAR_ERROR) { goto arc_error2; }; if (Tcl_GetDouble(wi->interp, elems[2], &arc->coords[1].x) == RADAR_ERROR) { goto arc_error2; }; if (Tcl_GetDouble(wi->interp, elems[3], &arc->coords[1].y) == RADAR_ERROR) { goto arc_error2; }; (*args)++; (*argc)--; #ifndef PTK Tcl_Free((char *) elems); #else if (freeProc) { (*freeProc)(num_elems, elems); } #endif arc->fill_color = RadarGetColorByValue(wi->win, wi->fore_color); arc->line_color = RadarGetColorByValue(wi->win, wi->fore_color); return RADAR_OK; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(Item item) { ArcItem arc = (ArcItem) item; WidgetInfo *wi = item->wi; char *text; if (strlen(arc->tile_name) != 0) { text = RadarMalloc((strlen(arc->tile_name) + 1) * sizeof(char)); strcpy(text, arc->tile_name); arc->tile_name = text; arc->tile = Tk_GetImage(wi->interp, wi->win, arc->tile_name, ArcTileChange, (ClientData) arc); } if (arc->line_pattern != RadarUnspecifiedPattern) { arc->line_pattern = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, arc->line_pattern)); } if (arc->fill_pattern != RadarUnspecifiedPattern) { arc->fill_pattern = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, arc->fill_pattern)); } arc->line_color = RadarGetColorByValue(wi->win, arc->line_color); arc->fill_color = RadarGetColorByValue(wi->win, arc->fill_color); arc->render_shape = NULL; } /* ********************************************************************************** * * Destroy -- * ********************************************************************************** */ static void Destroy(Item item) { WidgetInfo *wi = item->wi; ArcItem arc = (ArcItem) item; if (arc->render_shape) { RadarListFree(arc->render_shape); } if (strlen(arc->tile_name) != 0) { RadarFree(arc->tile_name); } if (arc->tile != RadarUnspecifiedImage) { Tk_FreeImage(arc->tile); arc->tile = RadarUnspecifiedImage; } if (arc->line_pattern != RadarUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, arc->line_pattern); } if (arc->fill_pattern != RadarUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, arc->fill_pattern); } RadarFreeColor(arc->fill_color); RadarFreeColor(arc->line_color); } /* ********************************************************************************** * * Configure -- * ********************************************************************************** */ static int Configure(Item item, int argc, RadarAttrList argv, int *flags) { WidgetInfo *wi = item->wi; ArcItem arc = (ArcItem) item; if (ITEM_P.ConfigureAttributes((char *) item, -1, argc, argv, flags) == RADAR_ERROR) { return RADAR_ERROR; } if (ISSET(*flags, RADAR_TILE_FLAG)) { Tk_Image tile; if (strcmp(arc->tile_name, "") != 0) { tile = Tk_GetImage(wi->interp, wi->win, arc->tile_name, ArcTileChange, (ClientData) arc); if (tile == NULL) { /* * The name will not be in sync with the image in * this case. */ return RADAR_ERROR; } } else { tile = RadarUnspecifiedImage; } if (arc->tile != RadarUnspecifiedImage) { Tk_FreeImage(arc->tile); } arc->tile = tile; } 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; } /* ********************************************************************************** * * ComputeCoordinates -- * ********************************************************************************** */ static void ComputeCoordinates(Item item, RadarBool force) { WidgetInfo *wi = item->wi; ArcItem arc = (ArcItem) item; RadarReal angle, sin1, cos1, sin2, cos2; int tmp, w_2, h_2, center_x, center_y,i; RadarPoint *p_list, p; RadarReal width_2, height_2, ox, oy; ResetBBox(&item->item_bounding_box); /* * If it is neither filled nor outlined, then nothing to show. */ if (!arc->line_width && ISCLEAR(arc->flags, FILLED_BIT)) { return; } /* * Special casing for rotation. This should eventually be the * only code but it lacks the arc capability. */ RadarTransfoDecompose(wi->current_transfo, NULL, NULL, &angle, NULL); if (angle >= PRECISION_LIMIT) { if (!arc->render_shape) { arc->render_shape = RadarListNew(PTS_IN_SHAPE, sizeof(RadarPoint)); RadarListAssertSize(arc->render_shape, PTS_IN_SHAPE); } p_list = (RadarPoint *) RadarListArray(arc->render_shape); width_2 = (arc->coords[1].x - arc->coords[0].x)/2.0; height_2 = (arc->coords[1].y - arc->coords[0].y)/2.0; ox = (arc->coords[1].x + arc->coords[0].x)/2.0; oy = (arc->coords[1].y + arc->coords[0].y)/2.0; for (i = 0; i < PTS_IN_SHAPE; i++, p_list++) { p.x = ox + oval_shape[i].x*width_2; p.y = oy + oval_shape[i].y*height_2; RadarTransformPoint(wi->current_transfo, &p, p_list); AddPointToBBox(&item->item_bounding_box, p_list->x, p_list->y); } tmp = (arc->line_width + 1) / 2 + 1; item->item_bounding_box.orig.x -= tmp; item->item_bounding_box.orig.y -= tmp; item->item_bounding_box.corner.x += tmp; item->item_bounding_box.corner.y += tmp; return; } RadarTransformPoint(wi->current_transfo, &arc->coords[0], &arc->orig); RadarTransformPoint(wi->current_transfo, &arc->coords[1], &arc->corner); if (arc->orig.x > arc->corner.x) { tmp = arc->orig.x; arc->orig.x = arc->corner.x; arc->corner.x = tmp; } if (arc->orig.y > arc->corner.y) { tmp = arc->orig.y; arc->orig.y = arc->corner.y; arc->corner.y = tmp; } /* * now compute the two points at the centers of the ends of the arc. * We first compute the position for a unit circle and then scale * to fit the shape. * Angles are running counter clockwise and y coordinates are inverted. * To handle these, the angles are negated. */ angle = -(arc->start_angle * M_PI / 180.0); sin1 = sin(angle); cos1 = cos(angle); angle -= (arc->angle_extent * M_PI / 180.0); sin2 = sin(angle); cos2 = cos(angle); w_2 = (arc->corner.x - arc->orig.x) / 2; h_2 = (arc->corner.y - arc->orig.y) / 2; center_x = (arc->corner.x + arc->orig.x) / 2; center_y = (arc->corner.y + arc->orig.y) / 2; arc->center1.x = center_x + REAL_TO_INT(cos1*w_2); arc->center1.y = center_y + REAL_TO_INT(sin1*h_2); arc->center2.x = center_x + REAL_TO_INT(cos2*w_2); arc->center2.y = center_y + REAL_TO_INT(sin2*h_2); /* * Add the ends centers to the bbox. */ AddPointToBBox(&item->item_bounding_box, arc->center1.x, arc->center1.y); AddPointToBBox(&item->item_bounding_box, arc->center2.x, arc->center2.y); /* * If the arc is filled or if the outline is closed in pie slice, * add the center of the arc. */ if ((ISSET(arc->flags, FILLED_BIT) || ISSET(arc->flags, CLOSED_BIT)) && ISSET(arc->flags, PIE_SLICE_BIT)) { AddPointToBBox(&item->item_bounding_box, center_x, center_y); } /* * Then add the 3-o'clock, 6-o'clock, 9-o'clock, 12-o'clock position * as required. */ tmp = -arc->start_angle; if (tmp < 0) { tmp += 360; } if ((tmp < arc->angle_extent) || ((tmp - 360) > arc->angle_extent)) { AddPointToBBox(&item->item_bounding_box, arc->corner.x, center_y); } tmp = 90 - arc->start_angle; if (tmp < 0) { tmp += 360; } if ((tmp < arc->angle_extent) || ((tmp - 360) > arc->angle_extent)) { AddPointToBBox(&item->item_bounding_box, center_x, arc->orig.y); } tmp = 180 - arc->start_angle; if (tmp < 0) { tmp += 360; } if ((tmp < arc->angle_extent) || ((tmp - 360) > arc->angle_extent)) { AddPointToBBox(&item->item_bounding_box, arc->orig.x, center_y); } tmp = 270 - arc->start_angle; if (tmp < 0) { tmp += 360; } if ((tmp < arc->angle_extent) || ((tmp - 360) > arc->angle_extent)) { AddPointToBBox(&item->item_bounding_box, center_x, arc->corner.y); } /* * Now take care of the arc outline width plus one pixel of margin. */ tmp = (arc->line_width + 1) / 2 + 1; item->item_bounding_box.orig.x -= tmp; item->item_bounding_box.orig.y -= tmp; item->item_bounding_box.corner.x += tmp; item->item_bounding_box.corner.y += tmp; } /* ********************************************************************************** * * 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) { ArcItem arc = (ArcItem) item; RadarPoint center; RadarBBox tran_rect; int rx, ry; int lw = arc->line_width; /* * Translate both the arc and the area so that they are * centered on the origin. rx,ry are the radiuses in x and y. */ center.x = (arc->corner.x + arc->orig.x) / 2; center.y = (arc->corner.y + arc->orig.y) / 2; tran_rect.orig.x = area->orig.x - center.x; tran_rect.orig.y = area->orig.y - center.y; rx = arc->corner.x - center.x + lw/2; ry = arc->corner.y - center.y + lw/2; return -1; } /* ********************************************************************************** * * Draw -- * ********************************************************************************** */ static void Draw(Item item) { WidgetInfo *wi = item->wi; ArcItem arc = (ArcItem) item; XGCValues values; int width, height; RadarPoint *p; XPoint xp[PTS_IN_SHAPE+1]; int num_points, i; width = arc->corner.x - arc->orig.x; height = arc->corner.y - arc->orig.y; if (arc->render_shape && (ISSET(arc->flags, FILLED_BIT) || (arc->line_width))) { p = (RadarPoint *) RadarListArray(arc->render_shape); num_points = RadarListSize(arc->render_shape); for (i = 0; i < num_points; i++, p++) { xp[i].x = (short) p->x; xp[i].y = (short) p->y; } xp[i] = xp[0]; num_points++; } /* Fill if requested */ if (ISSET(arc->flags, FILLED_BIT)) { values.foreground = RadarPixel(arc->fill_color); values.arc_mode = ISSET(arc->flags, PIE_SLICE_BIT) ? ArcPieSlice : ArcChord; if (arc->tile != RadarUnspecifiedImage) { /* Fill Tiled */ Pixmap pmap = GetImagePixmap(wi->win, arc->tile); values.fill_style = FillTiled; values.tile = pmap; values.ts_x_origin = (int) item->item_bounding_box.orig.x; values.ts_y_origin = (int) item->item_bounding_box.orig.y; XChangeGC(wi->dpy, wi->gc, GCTileStipXOrigin|GCTileStipYOrigin|GCFillStyle|GCTile|GCArcMode, &values); } else if (arc->fill_pattern != RadarUnspecifiedPattern) { /* Fill stippled */ values.fill_style = FillStippled; values.stipple = arc->fill_pattern; values.ts_x_origin = (int) item->item_bounding_box.orig.x; values.ts_y_origin = (int) item->item_bounding_box.orig.y; XChangeGC(wi->dpy, wi->gc, GCTileStipXOrigin|GCTileStipYOrigin|GCFillStyle|GCStipple|GCForeground|GCArcMode, &values); } else { /* Fill solid */ values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCForeground|GCFillStyle|GCArcMode, &values); } if (arc->render_shape) { XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xp, num_points, Convex, CoordModeOrigin); } else { XFillArc(wi->dpy, wi->draw_buffer, wi->gc, arc->orig.x, arc->orig.y, width, height, arc->start_angle*64, arc->angle_extent*64); } } /* * Draw the arc. */ if (arc->line_width) { SetLineStyle(wi->dpy, wi->gc, arc->line_style); values.foreground = RadarPixel(arc->line_color); values.line_width = (arc->line_width == 1) ? 0 : arc->line_width; values.cap_style = CapRound; values.join_style = JoinRound; if (arc->line_pattern == RadarUnspecifiedPattern) { values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCLineWidth|GCCapStyle|GCJoinStyle|GCForeground, &values); } else { values.fill_style = FillStippled; values.stipple = arc->line_pattern; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCStipple|GCLineWidth|GCCapStyle|GCJoinStyle|GCForeground, &values); } if (arc->render_shape) { XDrawLines(wi->dpy, wi->draw_buffer, wi->gc, xp, num_points, CoordModeOrigin); } else { XDrawArc(wi->dpy, wi->draw_buffer, wi->gc, arc->orig.x, arc->orig.y, width, height, arc->start_angle*64, arc->angle_extent*64); } /* * If the outline is closed, draw the closure. */ if (ISSET(arc->flags, CLOSED_BIT)) { if (ISSET(arc->flags, PIE_SLICE_BIT)) { XPoint points[3]; points[0].x = arc->center1.x; points[0].y = arc->center1.y; points[1].x = (arc->corner.x + arc->orig.x) / 2; points[1].y = (arc->corner.y + arc->orig.y) / 2; points[2].x = arc->center2.x; points[2].y = arc->center2.y; XDrawLines(wi->dpy, wi->draw_buffer, wi->gc, points, 3, CoordModeOrigin); } else { XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, arc->center1.x, arc->center1.y, arc->center2.x, arc->center2.y); } } } } /* ********************************************************************************** * * 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 *a_part) { ArcItem arc = (ArcItem) item; double dist, new_dist; RadarBool point_in_angle, filled, closed; RadarBool in_triangle, acute_angle; RadarPoint p1, center; int width, height; RadarDim lw = arc->line_width; center.x = (arc->corner.x + arc->orig.x) / 2; center.y = (arc->corner.y + arc->orig.y) / 2; width = arc->corner.x - arc->orig.x; height = arc->corner.y - arc->orig.y; /* * Let see if the point is in the angular range. First * transform the coordinates so that the oval is circular. * The 8192 factor avoids rounding errors. */ p1.y = ((int)(p->y - center.y)*8192) / height; p1.x = ((int)(p->x - center.x)*8192) / width; point_in_angle = PointInAngle(arc->start_angle, arc->angle_extent, &p1); /* * Now try to compute the distance dealing with the * many possible configurations. */ filled = !ISCLEAR(arc->flags, FILLED_BIT); closed = !ISCLEAR(arc->flags, CLOSED_BIT); /* * First the case of an arc not filled, not closed. We suppose * here that the outline is drawn since we cannot get here without * filling or outlining. */ if (!filled && !closed) { if (point_in_angle) { dist = OvalToPointDist(¢er, width, height, lw, p); if (dist < 0.0) { dist = -dist; } return dist; } dist = hypot((p->x - arc->center1.x), (p->y - arc->center1.y)); new_dist = hypot((p->x - arc->center2.x), (p->y - arc->center2.y)); if (new_dist < dist) { dist = new_dist; } /* Take into account CapRounded path. */ if (lw > 1) { dist -= lw/2; if (dist < 0.0) { dist = 0.0; } } return dist; } /* * Try to deal with filled and/or outline-closed arcs (not having full * angular extent). */ if (ISSET(arc->flags, PIE_SLICE_BIT)) { dist = LineToPointDist(¢er, &arc->center1, p); new_dist = LineToPointDist(¢er, &arc->center2, p); if (new_dist < dist) { dist = new_dist; } if (arc->line_width > 1) { if (closed) { dist -= arc->line_width/2; } /* * The arc outline is CapRounded so if it is not * full extent, includes the caps. */ else { new_dist = hypot(p->x - arc->center1.x, p->y - arc->center1.y) - lw/2; if (new_dist < dist) { dist = new_dist; } new_dist = hypot(p->x - arc->center2.x, p->y - arc->center2.y) - lw/2; if (new_dist < dist) { dist = new_dist; } } } if (dist <= 0.0) { return 0.0; } if (point_in_angle) { new_dist = OvalToPointDist(¢er, width, height, lw, p); if (new_dist < dist) { dist = new_dist; } if (dist < 0.0) { dist = filled ? 0.0 : -dist; } } return dist; } /* * This is a chord closed oval. */ dist = LineToPointDist(&arc->center1, &arc->center2, p); if (arc->line_width > 1) { if (closed) { dist -= arc->line_width/2; } /* * The arc outline is CapRounded so if it is not * full extent, includes the caps. */ else { new_dist = hypot(p->x - arc->center1.x, p->y - arc->center1.y) - lw/2; if (new_dist < dist) { dist = new_dist; } new_dist = hypot(p->x - arc->center2.x, p->y - arc->center2.y) - lw/2; if (new_dist < dist) { dist = new_dist; } } } if (dist <= 0.0) { return 0.0; } /* * Need to check the point against the triangle formed * by the difference between Chord mode and PieSlice mode. * This triangle needs to be included if extend is more than * 180 degrees and excluded otherwise. We try to see if * the center of the arc and the point are both on the same * side of the chord. If they are, the point is in the triangle */ if (arc->center1.x == arc->center2.x) { in_triangle = ((center.x <= arc->center1.x) && (p->x <= arc->center1.x)) || ((center.x > arc->center1.x) && (p->x > arc->center1.x)); } else { double a, b; a = ((double) (arc->center2.y - arc->center1.y)) / ((double) (arc->center2.x - arc->center1.x)); b = arc->center1.y - a*arc->center1.x; in_triangle = (((a*center.x + b - center.y) >= 0.0) == ((a*p->x + b - p->y) >= 0.0)); } acute_angle = ((arc->angle_extent > -180) && (arc->angle_extent < 180)); if (!point_in_angle && !acute_angle && filled && in_triangle) { return 0.0; } if (point_in_angle && (!acute_angle || !in_triangle)) { new_dist = OvalToPointDist(¢er, width, height, lw, p); if (new_dist < dist) { dist = new_dist; } if (dist < 0.0) { dist = filled ? 0.0 : -dist; } } return dist; } /* ********************************************************************************** * * GetClipVertices -- * Get the clipping shape. * ********************************************************************************** */ static RadarBool GetClipVertices(Item item, RadarPoint **points, int *num_points) { return False; } /* ********************************************************************************** * * Coords -- * Return or edit the item vertices. * ********************************************************************************** */ static int Coords(Item item, int index, int cmd, RadarPoint **pts, int *num_pts) { ArcItem arc = (ArcItem) item; if ((cmd == COORDS_ADD) || (cmd == COORDS_ADD_LAST) || (cmd == COORDS_REMOVE)) { Tcl_AppendResult(item->wi->interp, " arcs can't add or remove vertices", NULL); return RADAR_ERROR; } else if (cmd == COORDS_REPLACE_ALL) { if (*num_pts != 2) { Tcl_AppendResult(item->wi->interp, " coords command need 2 points on arcs", NULL); return RADAR_ERROR; } arc->coords[0] = (*pts)[0]; arc->coords[1] = (*pts)[1]; ITEM.Invalidate(item, RADAR_COORDS_FLAG); } else if (cmd == COORDS_REPLACE) { if (*num_pts < 1) { Tcl_AppendResult(item->wi->interp, " coords command need at least 1 point", NULL); return RADAR_ERROR; } if (index < 0) { index += 2; } if ((index < 0) || (index > 1)) { range_err: Tcl_AppendResult(item->wi->interp, " incorrect coord index, should be between -2 and 1", NULL); return RADAR_ERROR; } arc->coords[index] = (*pts)[0]; ITEM.Invalidate(item, RADAR_COORDS_FLAG); } else if (cmd == COORDS_READ_ALL) { *num_pts = 2; *pts = arc->coords; } else if (cmd == COORDS_READ) { if (index < 0) { index += 2; } if ((index < 0) || (index > 1)) { goto range_err; } *num_pts = 1; *pts = &arc->coords[index]; } return RADAR_OK; } /* ********************************************************************************** * * PostScript -- * ********************************************************************************** */ static void PostScript(Item item, PostScriptInfo ps_info) { } /* ********************************************************************************** * * Exported functions struct -- * ********************************************************************************** */ static ItemClassStruct ARC_ITEM_CLASS = { sizeof(ArcItemStruct), False, False, False, "arc", arc_attrs, Init, Clone, Destroy, Configure, Query, NULL, NULL, GetClipVertices, Coords, ComputeCoordinates, ToArea, Draw, IsSensitive, Pick, PostScript }; RadarItemClassId RadarArc = (RadarItemClassId) &ARC_ITEM_CLASS;