/* * Track.c -- Implementation of Track and WayPoint items. * * Authors : Patrick Lecoanet. * Creation date : * * $Id$ */ /* * Copyright (c) 1993 - 2005 CENA, Patrick Lecoanet -- * * See the file "Copyright" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include "Types.h" #include "Track.h" #include "Draw.h" #include "Geo.h" #include "Item.h" #include "Group.h" #include "WidgetInfo.h" #include "Image.h" #include "tkZinc.h" #include #include static const char rcsid[] = "$Id$"; static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; /* * Define this to enable overlap manager setting * the label distance rho. */ #undef DP /* * Some default values */ #define DEFAULT_MARKER_SIZE 0 #define DEFAULT_LABEL_ANGLE 20 #define DEFAULT_LABEL_DISTANCE 50 #define DEFAULT_LINE_WIDTH 1 #define DEFAULT_LABEL_PREFERRED_ANGLE 0 #define DEFAULT_CONVERGENCE_STYLE 0 #ifdef OM_TECH #define OMTECHSTUFFLEN 256 /* length of technical buffer for OM purpose */ #endif #define SPEED_VECTOR_PICKING_THRESHOLD 5 /* In pixels */ /* * Sets a threshold for calculating distance from label_dx, label_dy. * Above this threshold value, the module is discarded, label_distance * is preferred. */ #define LABEL_DISTANCE_THRESHOLD 5 #define MARKER_FILLED_BIT 1 << 0 #define FILLED_HISTORY_BIT 1 << 1 #define DOT_MIXED_HISTORY_BIT 1 << 2 #define CIRCLE_HISTORY_BIT 1 << 3 #define SV_MARK_BIT 1 << 4 #define SV_TICKS_BIT 1 << 5 #define POLAR_BIT 1 << 6 #define FROZEN_LABEL_BIT 1 << 7 #define LAST_AS_FIRST_BIT 1 << 8 #define HISTORY_VISIBLE_BIT 1 << 9 #define CURRENT_POSITION -2 #define LEADER -3 #define CONNECTION -4 #define SPEED_VECTOR -5 /* ********************************************************************************** * * Specific Track item record * ********************************************************************************** */ typedef struct { ZnPoint world; /* world coord of pos */ ZnPoint dev; /* dev coord of pos */ ZnBool visible; } HistoryStruct, *History; typedef struct _TrackItemStruct { ZnItemStruct header; /* Public data */ unsigned short flags; ZnImage symbol; /* item symbol */ ZnGradient *symbol_color; int label_angle; /* Label angle from track (degree). */ ZnDim label_distance; /* Label distance from track. */ ZnDim label_dx; /* Label dx/dy from track. */ ZnDim label_dy; int label_preferred_angle; int label_convergence_style; Tk_Anchor label_anchor; ZnLeaderAnchors leader_anchors; /* Spec of the leader attachment */ ZnGradient *leader_color; /* leader color */ ZnLineStyle leader_style; ZnLineShape leader_shape; ZnLineEnd leader_first_end; ZnLineEnd leader_last_end; ZnDim leader_width; ZnDim marker_size; /* world size of error circle */ ZnGradient *marker_color; /* error circle color */ ZnLineStyle marker_style; /* error circle style */ ZnImage marker_fill_pattern; /* error circle fill pattern */ ZnGradient *connection_color; /* connection color */ ZnLineStyle connection_style; ZnDim connection_width; ZnGradient *speed_vector_color; /* s. v. color */ ZnPoint pos; /* item world coordinates */ ZnPoint speed_vector; /* s. v. slope in world coord */ ZnDim speed_vector_width; ZnGradient *history_color; ZnDim history_width; #ifdef OM_TECH char *omtechstuff; #endif /* Private data */ ZnFieldSetStruct field_set; ZnPoint dev; /* device coords of current pos */ ZnPoint speed_vector_dev; /* s. v. end in device coord */ ZnDim marker_size_dev; /* dev size of error circle */ ZnList history; /* pos list */ ZnList leader_points; } TrackItemStruct, *TrackItem; static ZnAttrConfig track_attrs[] = { { ZN_CONFIG_BOOL, "-catchevent", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_CATCH_EVENT_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_BOOL, "-circlehistory", NULL, Tk_Offset(TrackItemStruct, flags), CIRCLE_HISTORY_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-composealpha", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_COMPOSE_ALPHA_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-composerotation", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_COMPOSE_ROTATION_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-composescale", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_COMPOSE_SCALE_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_ITEM, "-connecteditem", NULL, Tk_Offset(TrackItemStruct, header.connected_item), 0, ZN_COORDS_FLAG|ZN_ITEM_FLAG, False }, { ZN_CONFIG_GRADIENT, "-connectioncolor", NULL, Tk_Offset(TrackItemStruct, connection_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-connectionsensitive", NULL, Tk_Offset(TrackItemStruct, header.part_sensitive), ZnPartToBit(CONNECTION), ZN_REPICK_FLAG, False }, { ZN_CONFIG_LINE_STYLE, "-connectionstyle", NULL, Tk_Offset(TrackItemStruct, connection_style), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_DIM, "-connectionwidth", NULL, Tk_Offset(TrackItemStruct, connection_width), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-filledhistory", NULL, Tk_Offset(TrackItemStruct, flags), FILLED_HISTORY_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-filledmarker", NULL, Tk_Offset(TrackItemStruct, flags), MARKER_FILLED_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-frozenlabel", NULL, Tk_Offset(TrackItemStruct, flags), FROZEN_LABEL_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-historycolor", NULL, Tk_Offset(TrackItemStruct, history_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_DIM, "-historywidth", NULL, Tk_Offset(TrackItemStruct, history_width), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_ANCHOR, "-labelanchor", NULL, Tk_Offset(TrackItemStruct, label_anchor), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_ANGLE, "-labelangle", NULL, Tk_Offset(TrackItemStruct, label_angle), 0, ZN_COORDS_FLAG|ZN_POLAR_FLAG, False }, { ZN_CONFIG_UINT, "-labelconvergencestyle", NULL, Tk_Offset(TrackItemStruct, label_convergence_style), 0, 0, False }, { ZN_CONFIG_DIM, "-labeldistance", NULL, Tk_Offset(TrackItemStruct, label_distance), 0, ZN_COORDS_FLAG|ZN_POLAR_FLAG, False }, { ZN_CONFIG_DIM, "-labeldx", NULL, Tk_Offset(TrackItemStruct, label_dx), 0, ZN_COORDS_FLAG|ZN_CARTESIAN_FLAG, False }, { ZN_CONFIG_DIM, "-labeldy", NULL, Tk_Offset(TrackItemStruct, label_dy), 0, ZN_COORDS_FLAG|ZN_CARTESIAN_FLAG, False }, { ZN_CONFIG_LABEL_FORMAT, "-labelformat", NULL, Tk_Offset(TrackItemStruct, field_set.label_format), 0, ZN_COORDS_FLAG|ZN_CLFC_FLAG, False }, { ZN_CONFIG_ANGLE, "-labelpreferredangle", NULL, Tk_Offset(TrackItemStruct, label_preferred_angle), 0, 0, False }, { ZN_CONFIG_BOOL, "-lastasfirst", NULL, Tk_Offset(TrackItemStruct, flags), LAST_AS_FIRST_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_LEADER_ANCHORS, "-leaderanchors", NULL, Tk_Offset(TrackItemStruct, leader_anchors), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-leadercolor", NULL, Tk_Offset(TrackItemStruct, leader_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_LINE_END, "-leaderfirstend", NULL, Tk_Offset(TrackItemStruct, leader_first_end), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_LINE_END, "-leaderlastend", NULL, Tk_Offset(TrackItemStruct, leader_last_end), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-leadersensitive", NULL, Tk_Offset(TrackItemStruct, header.part_sensitive), ZnPartToBit(LEADER), ZN_REPICK_FLAG, False }, { ZN_CONFIG_LINE_STYLE, "-leaderstyle", NULL, Tk_Offset(TrackItemStruct, leader_style), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_LINE_SHAPE, "-leadershape", NULL, Tk_Offset(TrackItemStruct, leader_shape), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_DIM, "-leaderwidth", NULL, Tk_Offset(TrackItemStruct, leader_width), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-markercolor", NULL, Tk_Offset(TrackItemStruct, marker_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BITMAP, "-markerfillpattern", NULL, Tk_Offset(TrackItemStruct, marker_fill_pattern), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_DIM, "-markersize", NULL, Tk_Offset(TrackItemStruct, marker_size), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_LINE_STYLE, "-markerstyle", NULL, Tk_Offset(TrackItemStruct, marker_style), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-mixedhistory", NULL, Tk_Offset(TrackItemStruct, flags), DOT_MIXED_HISTORY_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_UINT, "-numfields", NULL, Tk_Offset(TrackItemStruct, field_set.num_fields), 0, 0, True }, { ZN_CONFIG_POINT, "-position", NULL, Tk_Offset(TrackItemStruct, pos), 0, ZN_COORDS_FLAG|ZN_MOVED_FLAG, False}, { ZN_CONFIG_PRI, "-priority", NULL, Tk_Offset(TrackItemStruct, header.priority), 0, ZN_DRAW_FLAG|ZN_REPICK_FLAG, False }, { ZN_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_SENSITIVE_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_POINT, "-speedvector", NULL, Tk_Offset(TrackItemStruct, speed_vector), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-speedvectorcolor", NULL, Tk_Offset(TrackItemStruct, speed_vector_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-speedvectormark", NULL, Tk_Offset(TrackItemStruct, flags), SV_MARK_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-speedvectorsensitive", NULL, Tk_Offset(TrackItemStruct, header.part_sensitive), ZnPartToBit(SPEED_VECTOR), ZN_REPICK_FLAG, False }, { ZN_CONFIG_BOOL, "-speedvectorticks", NULL, Tk_Offset(TrackItemStruct, flags), SV_TICKS_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_DIM, "-speedvectorwidth", NULL, Tk_Offset(TrackItemStruct, speed_vector_width), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BITMAP, "-symbol", NULL, Tk_Offset(TrackItemStruct, symbol), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-symbolcolor", NULL, Tk_Offset(TrackItemStruct, symbol_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-symbolsensitive", NULL, Tk_Offset(TrackItemStruct, header.part_sensitive), ZnPartToBit(CURRENT_POSITION), ZN_REPICK_FLAG, False }, { ZN_CONFIG_TAG_LIST, "-tags", NULL, Tk_Offset(TrackItemStruct, header.tags), 0, 0, False }, { ZN_CONFIG_BOOL, "-visible", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_VISIBLE_BIT, ZN_DRAW_FLAG|ZN_REPICK_FLAG|ZN_VIS_FLAG, False }, { ZN_CONFIG_BOOL, "-historyvisible", NULL, Tk_Offset(TrackItemStruct, flags), HISTORY_VISIBLE_BIT, ZN_COORDS_FLAG, False }, #ifdef OM_TECH /* This read only attribut receives reason value transmitted by ZnSetLabelAngleFromOm; it can be read by application to understand OM internal strategy */ { ZN_CONFIG_STRING, "-omtechstuff", NULL, Tk_Offset(TrackItemStruct, omtechstuff), 0, 0, True }, #endif { ZN_CONFIG_END, NULL, NULL, 0, 0, 0, False } }; static ZnAttrConfig wp_attrs[] = { { ZN_CONFIG_BOOL, "-catchevent", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_CATCH_EVENT_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_BOOL, "-composealpha", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_COMPOSE_ALPHA_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-composerotation", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_COMPOSE_ROTATION_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-composescale", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_COMPOSE_SCALE_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_ITEM, "-connecteditem", NULL, Tk_Offset(TrackItemStruct, header.connected_item), 0, ZN_COORDS_FLAG|ZN_ITEM_FLAG, False }, { ZN_CONFIG_GRADIENT, "-connectioncolor", NULL, Tk_Offset(TrackItemStruct, connection_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-connectionsensitive", NULL, Tk_Offset(TrackItemStruct, header.part_sensitive), ZnPartToBit(CONNECTION), ZN_REPICK_FLAG, False }, { ZN_CONFIG_LINE_STYLE, "-connectionstyle", NULL, Tk_Offset(TrackItemStruct, connection_style), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_DIM, "-connectionwidth", NULL, Tk_Offset(TrackItemStruct, connection_width), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-filledmarker", NULL, Tk_Offset(TrackItemStruct, flags), MARKER_FILLED_BIT, ZN_DRAW_FLAG, False }, { ZN_CONFIG_ANCHOR, "-labelanchor", NULL, Tk_Offset(TrackItemStruct, label_anchor), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_ANGLE, "-labelangle", NULL, Tk_Offset(TrackItemStruct, label_angle), 0, ZN_COORDS_FLAG|ZN_POLAR_FLAG, False }, { ZN_CONFIG_DIM, "-labeldistance", NULL, Tk_Offset(TrackItemStruct, label_distance), 0, ZN_COORDS_FLAG|ZN_POLAR_FLAG, False }, { ZN_CONFIG_DIM, "-labeldx", NULL, Tk_Offset(TrackItemStruct, label_dx), 0, ZN_COORDS_FLAG|ZN_CARTESIAN_FLAG, False }, { ZN_CONFIG_DIM, "-labeldy", NULL, Tk_Offset(TrackItemStruct, label_dy), 0, ZN_COORDS_FLAG|ZN_CARTESIAN_FLAG, False }, { ZN_CONFIG_LABEL_FORMAT, "-labelformat", NULL, Tk_Offset(TrackItemStruct, field_set.label_format), 0, ZN_COORDS_FLAG|ZN_CLFC_FLAG, False }, { ZN_CONFIG_LEADER_ANCHORS, "-leaderanchors", NULL, Tk_Offset(TrackItemStruct, leader_anchors), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-leadercolor", NULL, Tk_Offset(TrackItemStruct, leader_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_LINE_END, "-leaderfirstend", NULL, Tk_Offset(TrackItemStruct, leader_first_end), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_LINE_END, "-leaderlastend", NULL, Tk_Offset(TrackItemStruct, leader_last_end), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-leadersensitive", NULL, Tk_Offset(TrackItemStruct, header.part_sensitive), ZnPartToBit(LEADER), ZN_REPICK_FLAG, False }, { ZN_CONFIG_LINE_SHAPE, "-leadershape", NULL, Tk_Offset(TrackItemStruct, leader_shape), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_LINE_STYLE, "-leaderstyle", NULL, Tk_Offset(TrackItemStruct, leader_style), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_DIM, "-leaderwidth", NULL, Tk_Offset(TrackItemStruct, leader_width), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-markercolor", NULL, Tk_Offset(TrackItemStruct, marker_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BITMAP, "-markerfillpattern", NULL, Tk_Offset(TrackItemStruct, marker_fill_pattern), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_DIM, "-markersize", NULL, Tk_Offset(TrackItemStruct, marker_size), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_LINE_STYLE, "-markerstyle", NULL, Tk_Offset(TrackItemStruct, marker_style), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_UINT, "-numfields", NULL, Tk_Offset(TrackItemStruct, field_set.num_fields), 0, 0, True }, { ZN_CONFIG_POINT, "-position", NULL, Tk_Offset(TrackItemStruct, pos), 0, ZN_COORDS_FLAG, False}, { ZN_CONFIG_PRI, "-priority", NULL, Tk_Offset(TrackItemStruct, header.priority), 0, ZN_DRAW_FLAG|ZN_REPICK_FLAG, False }, { ZN_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_SENSITIVE_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_BITMAP, "-symbol", NULL, Tk_Offset(TrackItemStruct, symbol), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-symbolcolor", NULL, Tk_Offset(TrackItemStruct, symbol_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-symbolsensitive", NULL, Tk_Offset(TrackItemStruct, header.part_sensitive), ZnPartToBit(CURRENT_POSITION), ZN_REPICK_FLAG, False }, { ZN_CONFIG_TAG_LIST, "-tags", NULL, Tk_Offset(TrackItemStruct, header.tags), 0, 0, False }, { ZN_CONFIG_BOOL, "-visible", NULL, Tk_Offset(TrackItemStruct, header.flags), ZN_VISIBLE_BIT, ZN_DRAW_FLAG|ZN_REPICK_FLAG|ZN_VIS_FLAG, False }, { ZN_CONFIG_END, NULL, NULL, 0, 0, 0, False } }; /* ********************************************************************************** * * Init -- * ********************************************************************************** */ static int Init(ZnItem item, int *argc, Tcl_Obj *CONST *args[]) { TrackItem track = (TrackItem) item; ZnFieldSet field_set = &track->field_set; ZnWInfo *wi = item->wi; int num_fields; /*printf("size of a track = %d\n", sizeof(TrackItemStruct));*/ SET(item->flags, ZN_VISIBLE_BIT); SET(item->flags, ZN_SENSITIVE_BIT); SET(item->flags, ZN_CATCH_EVENT_BIT); SET(item->flags, ZN_COMPOSE_ALPHA_BIT); SET(item->flags, ZN_COMPOSE_ROTATION_BIT); SET(item->flags, ZN_COMPOSE_SCALE_BIT); SET(item->part_sensitive, ZnPartToBit(CURRENT_POSITION)); SET(item->part_sensitive, ZnPartToBit(LEADER)); SET(item->part_sensitive, ZnPartToBit(CONNECTION)); SET(item->part_sensitive, ZnPartToBit(SPEED_VECTOR)); track->symbol_color = ZnGetGradientByValue(wi->fore_color); track->symbol = ZnGetImageByValue(wi->track_symbol, NULL, NULL); track->label_anchor = TK_ANCHOR_CENTER; track->label_angle = DEFAULT_LABEL_ANGLE; track->label_distance = DEFAULT_LABEL_DISTANCE; SET(track->flags, POLAR_BIT); CLEAR(track->flags, FROZEN_LABEL_BIT); #ifdef OM_TECH track->omtechstuff = NULL; #endif track->label_preferred_angle = DEFAULT_LABEL_PREFERRED_ANGLE; track->label_convergence_style = DEFAULT_CONVERGENCE_STYLE ; track->leader_anchors = NULL; track->leader_color = ZnGetGradientByValue(wi->fore_color); track->leader_style = ZN_LINE_SIMPLE; track->leader_shape = ZN_LINE_STRAIGHT; track->leader_width = DEFAULT_LINE_WIDTH; track->connection_color = ZnGetGradientByValue(wi->fore_color); track->connection_style = ZN_LINE_SIMPLE; track->connection_width = DEFAULT_LINE_WIDTH; track->marker_color = ZnGetGradientByValue(wi->fore_color); track->marker_style = ZN_LINE_SIMPLE; track->marker_fill_pattern = ZnUnspecifiedImage; track->speed_vector_color = ZnGetGradientByValue(wi->fore_color); track->history_color = ZnGetGradientByValue(wi->fore_color); track->history_width = 8; CLEAR(track->flags, MARKER_FILLED_BIT); SET(track->flags, FILLED_HISTORY_BIT); CLEAR(track->flags, DOT_MIXED_HISTORY_BIT); CLEAR(track->flags, CIRCLE_HISTORY_BIT); CLEAR(track->flags, LAST_AS_FIRST_BIT); CLEAR(track->flags, SV_MARK_BIT); CLEAR(track->flags, SV_TICKS_BIT); if (item->class == ZnTrack) { item->priority = 1; SET(track->flags, HISTORY_VISIBLE_BIT); track->marker_size = DEFAULT_MARKER_SIZE; track->speed_vector.x = 0; track->speed_vector.y = 0; track->speed_vector_width = DEFAULT_LINE_WIDTH; } else { item->priority = 1; CLEAR(track->flags, HISTORY_VISIBLE_BIT); track->marker_size = 0; track->speed_vector.x = 0.0; track->speed_vector.y = 10.0; } field_set->item = item; field_set->label_format = NULL; /* * Then try to see if some fields are needed. */ if ((*argc > 0) && (Tcl_GetString((*args)[0])[0] != '-') && (Tcl_GetIntFromObj(wi->interp, (*args)[0], &num_fields) != TCL_ERROR)) { field_set->num_fields = num_fields; *args += 1; *argc -= 1; ZnFIELD.InitFields(field_set); } else { Tcl_AppendResult(wi->interp, " number of fields expected", NULL); return TCL_ERROR; } track->pos.x = 0; track->pos.y = 0; track->field_set.label_pos.x = 0; track->field_set.label_pos.y = 0; track->leader_first_end = NULL; track->leader_last_end = NULL; track->history = NULL; track->dev.x = 0; track->dev.y = 0; track->speed_vector_dev.x = 0; track->speed_vector_dev.y = 0; track->marker_size_dev = 0; track->leader_points = NULL; return TCL_OK; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(ZnItem item) { TrackItem track = (TrackItem) item; #ifdef OM_TECH char *omtechstuffclone; #endif if (track->history) { track->history = ZnListDuplicate(track->history); } track->dev.x = 0; track->dev.y = 0; track->speed_vector_dev.x = 0; track->speed_vector_dev.y = 0; track->marker_size_dev = 0; if (track->leader_points) { track->leader_points = ZnListDuplicate(track->leader_points); } if (track->leader_first_end) { ZnLineEndDuplicate(track->leader_first_end); } if (track->leader_last_end) { ZnLineEndDuplicate(track->leader_last_end); } ZnFIELD.CloneFields(&track->field_set); track->field_set.item = item; #ifdef OM_TECH if (track->omtechstuff) { omtechstuffclone = ZnMalloc( (OMTECHSTUFFLEN + 1) * sizeof(char)); strcpy(omtechstuffclone, track->omtechstuff); track->omtechstuff = omtechstuffclone; } #endif /* * We get all shared resources, colors bitmaps. */ track->symbol_color = ZnGetGradientByValue(track->symbol_color); track->leader_color = ZnGetGradientByValue(track->leader_color); track->connection_color = ZnGetGradientByValue(track->connection_color); track->marker_color = ZnGetGradientByValue(track->marker_color); track->speed_vector_color = ZnGetGradientByValue(track->speed_vector_color); track->history_color = ZnGetGradientByValue(track->history_color); if (track->symbol != ZnUnspecifiedImage) { track->symbol = ZnGetImageByValue(track->symbol, NULL, NULL); } if (track->marker_fill_pattern != ZnUnspecifiedImage) { track->marker_fill_pattern = ZnGetImageByValue(track->marker_fill_pattern, NULL, NULL); } } /* ********************************************************************************** * * Destroy -- * ********************************************************************************** */ static void Destroy(ZnItem item) { TrackItem track = (TrackItem) item; if (track->leader_points) { ZnListFree(track->leader_points); } if (track->leader_first_end) { ZnLineEndDelete(track->leader_first_end); } if (track->leader_last_end) { ZnLineEndDelete(track->leader_last_end); } if (track->history) { ZnListFree(track->history); } #ifdef OM_TECH if (track->omtechstuff) { ZnFree(track->omtechstuff); } #endif /* * Release shared resources. */ ZnFreeGradient(track->symbol_color); ZnFreeGradient(track->leader_color); ZnFreeGradient(track->connection_color); ZnFreeGradient(track->marker_color); ZnFreeGradient(track->speed_vector_color); ZnFreeGradient(track->history_color); if (track->symbol != ZnUnspecifiedImage) { ZnFreeImage(track->symbol, NULL, NULL); track->symbol = ZnUnspecifiedImage; } if (track->marker_fill_pattern != ZnUnspecifiedImage) { ZnFreeImage(track->marker_fill_pattern, NULL, NULL); track->marker_fill_pattern = ZnUnspecifiedImage; } ZnFIELD.FreeFields(&track->field_set); } /* ********************************************************************************** * * Configure -- * ********************************************************************************** */ static void AddToHistory(TrackItem track, ZnPoint old_pos) { ZnWInfo *wi = ((ZnItem) track)->wi; if (track->history) { HistoryStruct hist; hist.world = old_pos; hist.dev = track->dev; hist.visible = True; ZnListAdd(track->history, &hist, ZnListHead); ZnListTruncate(track->history, wi->track_managed_history_size); } else { /* We do not shift the first time we move as the preceding position * is not valid. */ /*printf("creating history\n");*/ track->history = ZnListNew(wi->track_managed_history_size+1, sizeof(HistoryStruct)); } } static int Configure(ZnItem item, int argc, Tcl_Obj *CONST argv[], int *flags) { TrackItem track = (TrackItem) item; ZnWInfo *wi = item->wi; ZnItem old_connected; ZnPoint old_pos; old_pos = track->pos; old_connected = item->connected_item; if (ZnConfigureAttributes(wi, item, item, track_attrs, argc, argv, flags) == TCL_ERROR) { return TCL_ERROR; } if (track->label_angle < 0) { track->label_angle = 360 + track->label_angle; } /* * Adapt to the new label locating system. */ if (ISSET(*flags, ZN_POLAR_FLAG)) { SET(track->flags, POLAR_BIT); ZnGroupSetCallOm(item->parent, True); } else if (ISSET(*flags, ZN_CARTESIAN_FLAG)) { CLEAR(track->flags, POLAR_BIT); ZnGroupSetCallOm(item->parent, True); } if (ISSET(*flags, ZN_ITEM_FLAG)) { /* * If the new connected item is not appropriate back up * to the old one. */ if ((item->connected_item == ZN_NO_ITEM) || (((item->connected_item->class == ZnTrack) || (item->connected_item->class == ZnWayPoint)) && (item->parent == item->connected_item->parent))) { ZnITEM.UpdateItemDependency(item, old_connected); } else { item->connected_item = old_connected; } } if (ISSET(*flags, ZN_VIS_FLAG)) { /* Record the change to trigger the overlap manager latter */ if ((item->class == ZnTrack) && ISSET(item->flags, ZN_VISIBLE_BIT)) { ZnGroupSetCallOm(item->parent, True); } } /* If the current position has changed, shift the past pos. */ if (ISSET(*flags, ZN_MOVED_FLAG)) { if (item->class == ZnTrack) { AddToHistory(track, old_pos); ZnGroupSetCallOm(item->parent, True); } } return TCL_OK; } /* ********************************************************************************** * * Query -- * ********************************************************************************** */ static int Query(ZnItem item, int argc, Tcl_Obj *CONST argv[]) { if (ZnQueryAttribute(item->wi->interp, item, track_attrs, argv[0]) == TCL_ERROR) { return TCL_ERROR; } return TCL_OK; } static void AdjustLabelDistance(TrackItem track, ZnTransfo *current_t, ZnReal theta, // Angle to adjust for ZnDim bb_width, ZnDim bb_height, ZnPoint *label_pos, ZnReal *label_dx, ZnReal *label_dy) { ZnReal rho, rotation, dist; int it; ZnBBox bbox; rho = track->label_distance; /* * Compute heading after applying the transform. */ ZnTransfoDecompose(current_t, NULL, NULL, &rotation, NULL); /*printf("rotation=%g, heading=%g, angle=%d\n", rotation, ZnProjectionToAngle(track->speed_vector.x, track->speed_vector.y), track->label_angle);*/ rotation = ZnProjectionToAngle(track->speed_vector.x, track->speed_vector.y)-rotation; /* * Adjust the distance to match the requested label_distance * whatever the label angle. */ it = 0; while (1) { ZnPointPolarToCartesian(rotation, rho, theta, label_dx, label_dy); label_pos->x = track->dev.x + *label_dx; label_pos->y = track->dev.y - *label_dy; ZnAnchor2Origin(label_pos, bb_width, bb_height, track->label_anchor, label_pos); ZnResetBBox(&bbox); ZnAddPointToBBox(&bbox, label_pos->x, label_pos->y); ZnAddPointToBBox(&bbox, label_pos->x + bb_width, label_pos->y + bb_height); dist = ZnRectangleToPointDist(&bbox, &track->dev); dist = track->label_distance - dist; if (ABS(dist) < 1.0 || it > 5) { break; } it++; rho += dist; } } /* ********************************************************************************** * * ComputeCoordinates -- * ********************************************************************************** */ static void ComputeCoordinates(ZnItem item, ZnBool force) { ZnWInfo *wi = item->wi; TrackItem track = (TrackItem) item; ZnFieldSet field_set = &track->field_set; ZnItem c_item; History hist; ZnPoint old_label_pos, old_pos, p, xp; ZnDim old_label_width, old_label_height; ZnReal rotation; ZnBBox bbox; ZnPoint *points; unsigned int num_points, num_acc_pos, i; int alignment, w_int, h_int; ZnReal w2=0.0, h2=0.0, w=0.0, h=0.0; ZnResetBBox(&item->item_bounding_box); old_label_pos = field_set->label_pos; old_label_width = field_set->label_width; old_label_height = field_set->label_height; old_pos = track->dev; ZnTransformPoint(wi->current_transfo, &track->pos, &track->dev); track->dev.x = ZnNearestInt(track->dev.x); track->dev.y = ZnNearestInt(track->dev.y); /*printf("track pos %g %g --> %g %g\n", track->pos.x, track->pos.y, track->dev.x, track->dev.y);*/ if (track->symbol != ZnUnspecifiedImage) { ZnSizeOfImage(track->symbol, &w_int, &h_int); /*printf("taille symbole %d %d\n", w, h);*/ w2 = (w_int+1.0)/2.0; h2 = (h_int+1.0)/2.0; bbox.orig.x = track->dev.x - w2; bbox.orig.y = track->dev.y - h2; bbox.corner.x = track->dev.x + w2; bbox.corner.y = track->dev.y + h2; ZnAddBBoxToBBox(&item->item_bounding_box, &bbox); } /* Here we approximate the past position sizes to the size of the current position. They are actually smaller but who care :-). In fact it is even worse as we use the overall information from the symbol font. */ if ((item->class == ZnTrack) && track->history) { unsigned int visible_history_size; /* * Trunc the visible history to the managed size. */ ZnListTruncate(track->history, wi->track_managed_history_size); visible_history_size = (ISSET(track->flags, HISTORY_VISIBLE_BIT) ? wi->track_visible_history_size : 0); ZnResetBBox(&bbox); w2 = (track->history_width+1.0)/2.0; num_acc_pos = ZnListSize(track->history); hist = ZnListArray(track->history); for (i = 0; i < num_acc_pos; i++) { ZnTransformPoint(wi->current_transfo, &hist[i].world, &hist[i].dev); if ((i < visible_history_size) && (hist[i].visible)) { bbox.orig.x = hist[i].dev.x - w2; bbox.orig.y = hist[i].dev.y - w2; bbox.corner.x = hist[i].dev.x + w2; bbox.corner.y = hist[i].dev.y + w2; ZnAddBBoxToBBox(&item->item_bounding_box, &bbox); } } } /* * Compute the speed vector end. */ if (item->class == ZnTrack) { p.x = track->pos.x + track->speed_vector.x * wi->speed_vector_length; p.y = track->pos.y + track->speed_vector.y * wi->speed_vector_length; ZnTransformPoint(wi->current_transfo, &p, &track->speed_vector_dev); track->speed_vector_dev.x = ZnNearestInt(track->speed_vector_dev.x); track->speed_vector_dev.y = ZnNearestInt(track->speed_vector_dev.y); if (ISSET(track->flags, SV_MARK_BIT)) { w = track->speed_vector_width + 1.0; ZnAddPointToBBox(&item->item_bounding_box, track->speed_vector_dev.x - w, track->speed_vector_dev.y - w); ZnAddPointToBBox(&item->item_bounding_box, track->speed_vector_dev.x + w, track->speed_vector_dev.y + w); } else { ZnAddPointToBBox(&item->item_bounding_box, track->speed_vector_dev.x, track->speed_vector_dev.y); } } /* * Take care of the connection between items. */ c_item = item->connected_item; if ((c_item != ZN_NO_ITEM) && (track->connection_width > 0.0)) { w2 = track->connection_width/2.0; //printf("%d connected to %d, %g %g and %g %g\n", c_item->id, item->id, // ((TrackItem)c_item)->dev.x, ((TrackItem)c_item)->dev.y, // track->dev.x, track->dev.y); ZnAddPointToBBox(&item->item_bounding_box, track->dev.x-w2, track->dev.y-w2); ZnAddPointToBBox(&item->item_bounding_box, ((TrackItem)c_item)->dev.x+w2, ((TrackItem)c_item)->dev.y+w2); } /* * Compute the size of the circular marker. */ p.x = track->pos.x + track->marker_size; p.y = track->pos.y; ZnTransformPoint(wi->current_transfo, &p, &xp); xp.x = xp.x - track->dev.x; xp.y = xp.y - track->dev.y; track->marker_size_dev = sqrt(xp.x*xp.x + xp.y*xp.y); track->marker_size_dev = ZnNearestInt(track->marker_size_dev); if (track->marker_size_dev > PRECISION_LIMIT) { ZnAddPointToBBox(&item->item_bounding_box, track->dev.x - (ZnPos) track->marker_size_dev, track->dev.y - (ZnPos) track->marker_size_dev); ZnAddPointToBBox(&item->item_bounding_box, track->dev.x + (ZnPos) track->marker_size_dev, track->dev.y + (ZnPos) track->marker_size_dev); } /* Compute the new label bounding box. */ if (field_set->label_format && field_set->num_fields) { ZnDim bb_width, bb_height; ZnReal theta, dist; ZnPoint leader_end; ZnFIELD.GetLabelBBox(field_set, &bb_width, &bb_height); /* * Compute the label position. */ if (ISSET(track->flags, POLAR_BIT)) { /* * Update label_dx, label_dy from label_distance, label_angle */ AdjustLabelDistance(track, wi->current_transfo, track->label_angle, bb_width, bb_height, &field_set->label_pos, &track->label_dx, &track->label_dy); } else { /* * Update label_angle following the change in label_dx, label_dy. * label_distance is not updated. */ ZnTransfoDecompose(wi->current_transfo, NULL, NULL, &rotation, NULL); rotation = ZnProjectionToAngle(track->speed_vector.x, track->speed_vector.y) - rotation; ZnPointCartesianToPolar(rotation, &dist, &theta, track->label_dx, track->label_dy); track->label_angle = (int) theta; field_set->label_pos.x = track->dev.x + track->label_dx; field_set->label_pos.y = track->dev.y - track->label_dy; ZnAnchor2Origin(&field_set->label_pos, bb_width, bb_height, track->label_anchor, &field_set->label_pos); } field_set->label_pos.x = ZnNearestInt(field_set->label_pos.x); field_set->label_pos.y = ZnNearestInt(field_set->label_pos.y); /* * Need to compensate for GL thick lines */ #ifdef GL #define CORR 1 #else #define CORR 0 #endif ZnAddPointToBBox(&item->item_bounding_box, field_set->label_pos.x - CORR, field_set->label_pos.y - CORR); ZnAddPointToBBox(&item->item_bounding_box, field_set->label_pos.x + (ZnPos) bb_width + CORR, field_set->label_pos.y + (ZnPos) bb_height + CORR); #undef CORR /* * Process the leader. */ if (track->leader_width > 0) { int left_x, left_y, right_x, right_y; ZnPoint end_points[ZN_LINE_END_POINTS]; /* * Compute the actual leader end in the label. */ if (track->leader_anchors) { left_x = track->leader_anchors->left_x; right_x = track->leader_anchors->right_x; left_y = track->leader_anchors->left_y; right_y = track->leader_anchors->right_y; } else { left_x = right_x = left_y = right_y = 50; } if (track->label_angle >= 270 || track->label_angle < 90) { if (track->leader_anchors && (left_y < 0)) { ZnFIELD.GetFieldBBox(field_set, (unsigned int) left_x, &bbox); leader_end.x = bbox.orig.x; leader_end.y = bbox.corner.y; } else { leader_end.x = field_set->label_pos.x + left_x*bb_width/100; leader_end.y = field_set->label_pos.y + left_y*bb_height/100; } alignment = ZN_AA_LEFT; } else { if (track->leader_anchors && (right_y < 0)) { ZnFIELD.GetFieldBBox(field_set, (unsigned int) right_x, &bbox); leader_end.x = bbox.corner.x; leader_end.y = bbox.corner.y; } else { leader_end.x = field_set->label_pos.x + right_x*bb_width/100; leader_end.y = field_set->label_pos.y + right_y*bb_height/100; } alignment = ZN_AA_RIGHT; } ZnFIELD.SetFieldsAutoAlign(field_set, alignment); /* Clip the leader on the label's fields */ ZnFIELD.LeaderToLabel(field_set, &track->dev, &leader_end); /* Setup leader shape points */ if (!track->leader_points) { track->leader_points = ZnListNew(ZN_LINE_SHAPE_POINTS, sizeof(ZnPoint)); } ZnLineShapePoints(&track->dev, &leader_end, track->leader_width, track->leader_shape, &bbox, track->leader_points); ZnAddBBoxToBBox(&item->item_bounding_box, &bbox); points = (ZnPoint *) ZnListArray(track->leader_points); num_points = ZnListSize(track->leader_points); /* Setup leader ends */ if (track->leader_first_end != NULL) { ZnGetLineEnd(&points[0], &points[1], track->leader_width, CapRound, track->leader_first_end, end_points); ZnAddPointsToBBox(&item->item_bounding_box, end_points, ZN_LINE_END_POINTS); } if (track->leader_last_end != NULL) { ZnGetLineEnd(&points[num_points-1], &points[num_points-2], track->leader_width, CapRound, track->leader_last_end, end_points); ZnAddPointsToBBox(&item->item_bounding_box, end_points, ZN_LINE_END_POINTS); } } } /* Update connected items. */ if ((old_label_pos.x != field_set->label_pos.x) || (old_label_pos.y != field_set->label_pos.y) || (old_label_width != field_set->label_width) || (old_label_height != field_set->label_height) || (old_pos.x != track->dev.x) || (old_pos.y != track->dev.y)) { /* Update connected items */ SET(item->flags, ZN_UPDATE_DEPENDENT_BIT); } } /* ********************************************************************************** * * ToArea -- * Tell if the object is entirely outside (-1), * entirely inside (1) or in between (0). * ********************************************************************************** */ static int ToArea(ZnItem item, ZnToArea ta) { TrackItem track = (TrackItem) item; int inside; int width, height; ZnDim lwidth, lheight; ZnBBox bbox, *area = ta->area; ZnPoint pts[2]; /* * Try the current position. */ ZnResetBBox(&bbox); if (track->symbol != ZnUnspecifiedImage) { ZnSizeOfImage(track->symbol, &width, &height); bbox.orig.x = track->dev.x-(width+1)/2; bbox.orig.y = track->dev.y-(height+1)/2; bbox.corner.x = bbox.orig.x + width; bbox.corner.y = bbox.orig.y + height; } inside = ZnBBoxInBBox(&bbox, area); if (inside == 0) { /*printf("track pos\n");*/ return 0; } /* * Try the fields. */ ZnFIELD.GetLabelBBox(&track->field_set, &lwidth, &lheight); if ((lwidth > 0.0) && (lheight > 0.0)) { if (ZnFIELD.FieldsToArea(&track->field_set, area) != inside) { return 0; } } /* * Try the leader. */ if (track->field_set.label_format && (track->leader_width > 0)) { ZnPoint end_points[ZN_LINE_END_POINTS]; ZnPoint *points; unsigned int num_points; points = (ZnPoint *) ZnListArray(track->leader_points); num_points = ZnListSize(track->leader_points); lwidth = track->leader_width > 1 ? track->leader_width : 0; if (ZnPolylineInBBox(points, num_points, lwidth, CapRound, JoinRound, area) != inside) { /*printf("track leader\n");*/ return 0; } if (track->leader_first_end != NULL) { ZnGetLineEnd(&points[0], &points[1], track->leader_width, CapRound, track->leader_first_end, end_points); if (ZnPolygonInBBox(end_points, ZN_LINE_END_POINTS, area, NULL) != inside) { /*printf("track leader\n");*/ return 0; } } if (track->leader_last_end != NULL) { ZnGetLineEnd(&points[num_points-1], &points[num_points-2], track->leader_width, CapRound, track->leader_last_end, end_points); if (ZnPolygonInBBox(end_points, ZN_LINE_END_POINTS, area, NULL) != inside) { /*printf("track leader\n");*/ return 0; } } } /* * Try the speed vector. */ if ((item->class == ZnTrack) && (track->speed_vector_width > 0)) { pts[0] = track->dev; pts[1] = track->speed_vector_dev; lwidth = track->speed_vector_width > 1 ? track->speed_vector_width : 0; if (ZnPolylineInBBox(pts, 2, lwidth, CapRound, JoinRound, area) != inside) { /*printf("track speed vector\n");*/ return 0; } } /* * Try the connection. */ if ((item->connected_item != ZN_NO_ITEM) && (track->connection_width > 0)) { pts[0] = track->dev; pts[1] = ((TrackItem) item->connected_item)->dev; lwidth = track->connection_width > 1 ? track->connection_width : 0; if (ZnPolylineInBBox(pts, 2, lwidth, CapRound, JoinRound, area) != inside) { /*printf("track connection\n");*/ return 0; } } return inside; } /* ********************************************************************************** * * Draw -- * ********************************************************************************** */ static void Draw(ZnItem item) { ZnWInfo *wi = item->wi; TrackItem track = (TrackItem) item; ZnItem c_item; XGCValues values; History hist; unsigned int h_side_size, side_size, width=0, height=0; unsigned int i, nb_hist, num_acc_pos; int x, y; /* Draw the marker */ if (track->marker_size_dev != 0) { ZnSetLineStyle(wi, track->marker_style); values.foreground = ZnGetGradientPixel(track->marker_color, 0.0); values.line_width = 1; if (ISSET(track->flags, MARKER_FILLED_BIT)) { if (track->marker_fill_pattern == ZnUnspecifiedImage) { /* Fill solid */ values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle | GCLineWidth | GCForeground, &values); } else { /* Fill stippled */ values.fill_style = FillStippled; values.stipple = ZnImagePixmap(track->marker_fill_pattern, wi->win); XChangeGC(wi->dpy, wi->gc, GCFillStyle | GCStipple | GCLineWidth | GCForeground, &values); } XFillArc(wi->dpy, wi->draw_buffer, wi->gc, (int) (track->dev.x - (ZnPos) track->marker_size_dev), (int) (track->dev.y - (ZnPos) track->marker_size_dev), (unsigned int) track->marker_size_dev * 2, (unsigned int) track->marker_size_dev * 2, 0, 360 * 64); } else { values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle | GCLineWidth | GCForeground, &values); XDrawArc(wi->dpy, wi->draw_buffer, wi->gc, (int) (track->dev.x - (ZnPos) track->marker_size_dev), (int) (track->dev.y - (ZnPos) track->marker_size_dev), (unsigned int) (track->marker_size_dev * 2), (unsigned int) (track->marker_size_dev * 2), 0, 360 * 64); } } /* * Draw the connection. */ c_item = item->connected_item; if ((c_item != ZN_NO_ITEM) && (track->connection_width > 0)) { ZnPoint pts[2]; pts[0] = track->dev; pts[1] = ((TrackItem) item->connected_item)->dev; ZnDrawLineShape(wi, pts, 2, track->connection_style, ZnGetGradientPixel(track->connection_color, 0.0), track->connection_width, ZN_LINE_STRAIGHT); } /* * Draw the speed vector. */ if ((item->class == ZnTrack) && (track->speed_vector_width > 0)) { values.foreground = ZnGetGradientPixel(track->speed_vector_color, 0.0); values.line_width = (int) track->speed_vector_width; values.line_style = LineSolid; values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCForeground | GCLineWidth | GCLineStyle | GCFillStyle, &values); XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, (int) track->dev.x, (int) track->dev.y, (int) track->speed_vector_dev.x, (int) track->speed_vector_dev.y); } /* * Draw the leader. */ if (track->field_set.label_format && (track->leader_width > 0)) { ZnPoint end_points[ZN_LINE_END_POINTS]; XPoint xpoints[ZN_LINE_END_POINTS]; ZnPoint *points; unsigned int num_points; points = (ZnPoint *) ZnListArray(track->leader_points); num_points = ZnListSize(track->leader_points); ZnDrawLineShape(wi, points, num_points, track->leader_style, ZnGetGradientPixel(track->leader_color, 0.0), track->leader_width, track->leader_shape); if (track->leader_first_end != NULL) { ZnGetLineEnd(&points[0], &points[1], track->leader_width, CapRound, track->leader_first_end, end_points); for (i = 0; i < ZN_LINE_END_POINTS; i++) { xpoints[i].x = (short) end_points[i].x; xpoints[i].y = (short) end_points[i].y; } XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xpoints, ZN_LINE_END_POINTS, Nonconvex, CoordModeOrigin); } if (track->leader_last_end != NULL) { ZnGetLineEnd(&points[num_points-1], &points[num_points-2], track->leader_width, CapRound, track->leader_last_end, end_points); for (i = 0; i < ZN_LINE_END_POINTS; i++) { xpoints[i].x = (short) end_points[i].x; xpoints[i].y = (short) end_points[i].y; } XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xpoints, ZN_LINE_END_POINTS, Nonconvex, CoordModeOrigin); } } if (track->symbol != ZnUnspecifiedImage) { ZnSizeOfImage(track->symbol, &width, &height); } /* * Draw the history, current pos excepted. */ if ((item->class == ZnTrack) && track->history) { unsigned int visible_history_size; visible_history_size = (ISSET(track->flags, HISTORY_VISIBLE_BIT) ? wi->track_visible_history_size : 0); values.foreground = ZnGetGradientPixel(track->history_color, 0.0); values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCForeground|GCFillStyle, &values); if (ISCLEAR(track->flags, FILLED_HISTORY_BIT)) { values.line_width = 1; values.line_style = LineSolid; XChangeGC(wi->dpy, wi->gc, GCLineWidth | GCLineStyle, &values); } num_acc_pos = MIN(visible_history_size, ZnListSize(track->history)); hist = ZnListArray(track->history); side_size = (int) track->history_width; for (i = 0, nb_hist = 0; i < num_acc_pos; i++) { if (ISSET(track->flags, LAST_AS_FIRST_BIT) && (i == visible_history_size-1)) { values.foreground = ZnGetGradientPixel(track->symbol_color, 0.0); XChangeGC(wi->dpy, wi->gc, GCForeground, &values); } side_size--; side_size = MAX(1, side_size); h_side_size = (side_size+1)/2; if (hist[i].visible) { if (ISSET(track->flags, DOT_MIXED_HISTORY_BIT) && !(nb_hist++ % 2)) { x = (int) hist[i].dev.x; y = (int) hist[i].dev.y; /* Draw a point (portability layer doesn't define a XDrawPoint) */ XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, x, y, x, y); } else { x = ((int) hist[i].dev.x) - h_side_size; y = ((int) hist[i].dev.y) - h_side_size; if (ISSET(track->flags, CIRCLE_HISTORY_BIT)) { if (ISSET(track->flags, FILLED_HISTORY_BIT)) { XFillArc(wi->dpy, wi->draw_buffer, wi->gc, x, y, side_size, side_size, 0, 360*64); } else { XDrawArc(wi->dpy, wi->draw_buffer, wi->gc, x, y, side_size - 1, side_size - 1, 0, 360*64); } } else { if (ISSET(track->flags, FILLED_HISTORY_BIT)) { XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, side_size, side_size); } else { XDrawRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, side_size - 1, side_size - 1); } } } } } } /* * Draw the current position using a pattern for Tk. */ if (track->symbol != ZnUnspecifiedImage) { x = ((int) track->dev.x) - (width+1)/2; y = ((int) track->dev.y) - (height+1)/2; values.foreground = ZnGetGradientPixel(track->symbol_color, 0.0); values.fill_style = FillStippled; values.stipple = ZnImagePixmap(track->symbol, wi->win); values.ts_x_origin = x; values.ts_y_origin = y; XChangeGC(wi->dpy, wi->gc, GCForeground|GCFillStyle|GCStipple|GCTileStipXOrigin|GCTileStipYOrigin, &values); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, width, height); } /* * Draw the label. */ ZnFIELD.DrawFields(&track->field_set); } /* ********************************************************************************** * * Render -- * ********************************************************************************** */ #ifdef GL struct MarkerCBData { ZnPoint *p; int num; ZnReal size; ZnPoint center; }; static void MarkerRenderCB(void *closure) { struct MarkerCBData *cbd = (struct MarkerCBData *) closure; int i; glBegin(GL_TRIANGLE_FAN); glVertex2d(cbd->center.x, cbd->center.y); for (i = 0; i < cbd->num; i++) { glVertex2d(cbd->center.x + cbd->p[i].x*cbd->size, cbd->center.y + cbd->p[i].y*cbd->size); } glEnd(); } static void Render(ZnItem item) { ZnWInfo *wi = item->wi; TrackItem track = (TrackItem) item; TrackItem c_item; History hist; unsigned int h_side_size, side_size, width=0, height=0; unsigned int i, j, nb_hist, num_acc_pos; unsigned short alpha; XColor *color; ZnPoint *points; unsigned int num_points; ZnReal x0, y0, size; /* Draw the marker */ if (track->marker_size_dev != 0) { points = ZnGetCirclePoints(3, ZN_CIRCLE_MEDIUM, 0.0, 2*M_PI, &num_points, NULL); x0 = track->dev.x; y0 = track->dev.y; size = track->marker_size_dev; if (ISSET(track->flags, MARKER_FILLED_BIT)) { ZnBBox bbox; struct MarkerCBData cbd; cbd.center.x = x0; cbd.center.y = y0; cbd.num = num_points; cbd.size = size; cbd.p = points; glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); if (track->marker_fill_pattern != ZnUnspecifiedImage) { /* Fill stippled */ ZnResetBBox(&bbox); ZnAddPointToBBox(&bbox, track->dev.x-size, track->dev.y-size); ZnAddPointToBBox(&bbox, track->dev.x+size, track->dev.y+size); ZnRenderTile(wi, track->marker_fill_pattern, track->marker_color, MarkerRenderCB, &cbd, (ZnPoint *) &bbox); } else { color = ZnGetGradientColor(track->marker_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); MarkerRenderCB(&cbd); } } else { glLineWidth(1.0); ZnSetLineStyle(wi, track->marker_style); glBegin(GL_LINE_LOOP); for (i = 0; i < num_points; i++) { glVertex2d(x0 + points[i].x*size, y0 + points[i].y*size); } glEnd(); } } /* * Draw the connection. */ c_item = (TrackItem) item->connected_item; if ((c_item != ZN_NO_ITEM) && (track->connection_width > 0)) { color = ZnGetGradientColor(track->connection_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); glLineWidth((GLfloat)track->connection_width); glBegin(GL_LINES); glVertex2d(track->dev.x, track->dev.y); glVertex2d(c_item->dev.x, c_item->dev.y); glEnd(); } /* * Draw the speed vector. */ if ((item->class == ZnTrack) && (track->speed_vector_width > 0)) { unsigned int num_clips=0, svlength=0; ZnReal svxstep=0, svystep=0; GLfloat ticksize=0; color = ZnGetGradientColor(track->speed_vector_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); glLineWidth((GLfloat)track->speed_vector_width); /* * Turn off AA to obtain a square point precisely defined */ if (ISSET(track->flags, SV_TICKS_BIT) || ISSET(track->flags, SV_MARK_BIT)) { glDisable(GL_POINT_SMOOTH); if (ISSET(track->flags, SV_TICKS_BIT)) { num_clips = ZnListSize(wi->clip_stack); ticksize = 3; svlength = (int) wi->speed_vector_length; svxstep = (track->speed_vector_dev.x-track->dev.x)/svlength; svystep = (track->speed_vector_dev.y-track->dev.y)/svlength; glPointSize(ticksize); ZnGlStartClip(num_clips, False); glBegin(GL_POINTS); for (i = 1; i < svlength; i++) { glVertex2d(track->dev.x + i*svxstep, track->dev.y + i*svystep); } glEnd(); ZnGlRenderClipped(); } } glBegin(GL_LINES); glVertex2d(track->dev.x, track->dev.y); glVertex2d(track->speed_vector_dev.x, track->speed_vector_dev.y); glEnd(); if (ISSET(track->flags, SV_MARK_BIT)) { glPointSize((GLfloat) (track->speed_vector_width + 2.0)); glBegin(GL_POINTS); glVertex2d(track->speed_vector_dev.x, track->speed_vector_dev.y); glEnd(); } if (ISSET(track->flags, SV_TICKS_BIT) || ISSET(track->flags, SV_MARK_BIT)) { glEnable(GL_POINT_SMOOTH); if (ISSET(track->flags, SV_TICKS_BIT)) { glPointSize(ticksize); ZnGlRestoreStencil(num_clips, False); glBegin(GL_POINTS); for (i = 1; i < svlength; i++) { glVertex2d(track->dev.x + i*svxstep, track->dev.y + i*svystep); } glEnd(); ZnGlEndClip(num_clips); } } } /* * Draw the leader. */ if (track->field_set.label_format && (track->leader_width > 0)) { points = ZnListArray(track->leader_points); num_points = ZnListSize(track->leader_points); ZnRenderPolyline(wi, points, num_points, track->leader_width, track->leader_style, CapRound, JoinRound, track->leader_first_end, track->leader_last_end, track->leader_color); } if (track->symbol != ZnUnspecifiedImage) { ZnSizeOfImage(track->symbol, &width, &height); } /* * Draw the history, current pos excepted. */ if ((item->class == ZnTrack) && track->history) { unsigned int visible_history_size; visible_history_size = (ISSET(track->flags, HISTORY_VISIBLE_BIT) ? wi->track_visible_history_size : 0); points = ZnGetCirclePoints(3, ZN_CIRCLE_COARSE, 0.0, 2*M_PI, &num_points, NULL); color = ZnGetGradientColor(track->history_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); glLineWidth(1.0); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); num_acc_pos = MIN(visible_history_size, ZnListSize(track->history)); hist = ZnListArray(track->history); side_size = (int) track->history_width; /* * Turning off line and point smoothing * to enhance ;-) history drawing. */ glDisable(GL_LINE_SMOOTH); glDisable(GL_POINT_SMOOTH); for (i = 0, nb_hist = 0; i < num_acc_pos; i++) { if (ISSET(track->flags, LAST_AS_FIRST_BIT) && (i == visible_history_size-1)) { color = ZnGetGradientColor(track->symbol_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); } side_size--; side_size = MAX(1, side_size); h_side_size = (side_size+1)/2; if (hist[i].visible) { x0 = hist[i].dev.x; y0 = hist[i].dev.y; if ((ISSET(track->flags, DOT_MIXED_HISTORY_BIT) && !(nb_hist++ % 2)) || (side_size == 1)) { glPointSize(1.0); glBegin(GL_POINTS); glVertex2d(x0, y0); glEnd(); } else { if (ISSET(track->flags, CIRCLE_HISTORY_BIT)) { if (ISSET(track->flags, FILLED_HISTORY_BIT)) { glPointSize((GLfloat) side_size); glBegin(GL_POINTS); glVertex2d(x0, y0); glEnd(); } else { #if 1 glBegin(GL_LINE_LOOP); for (j = 0; j < num_points; j++) { glVertex2d(x0 + points[j].x*h_side_size, y0 + points[j].y*h_side_size); } glEnd(); #else RenderHollowDot(wi, &hist[i].dev, side_size+1); #endif } } else { if (ISSET(track->flags, FILLED_HISTORY_BIT)) { glBegin(GL_QUADS); glVertex2d(x0 - h_side_size, y0 - h_side_size); glVertex2d(x0 - h_side_size, y0 + h_side_size); glVertex2d(x0 + h_side_size, y0 + h_side_size); glVertex2d(x0 + h_side_size, y0 - h_side_size); glEnd(); } else { glBegin(GL_LINE_LOOP); glVertex2d(x0 - h_side_size, y0 - h_side_size); glVertex2d(x0 - h_side_size, y0 + h_side_size); glVertex2d(x0 + h_side_size, y0 + h_side_size); glVertex2d(x0 + h_side_size, y0 - h_side_size); glEnd(); } } } } } glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); } /* * Draw the current position using a pattern. */ if (track->symbol != ZnUnspecifiedImage) { ZnPoint p; p.x = track->dev.x - (width+1)/2; p.y = track->dev.y - (height+1)/2; ZnRenderIcon(wi, track->symbol, track->symbol_color, &p, True); } /* * Render the label. */ ZnFIELD.RenderFields(&track->field_set); } #else static void Render(ZnItem item) { } #endif /* ********************************************************************************** * * IsSensitive -- * ********************************************************************************** */ static ZnBool IsSensitive(ZnItem item, int item_part) { if (ISCLEAR(item->flags, ZN_SENSITIVE_BIT) || !item->parent->class->IsSensitive(item->parent, ZN_NO_PART)) { return False; } if (item_part < ZN_NO_PART) { return ISSET(item->part_sensitive, ZnPartToBit(item_part)); } else if (item_part >= 0) { return ZnFIELD.IsFieldSensitive(&((TrackItem) item)->field_set, item_part); } else if (item_part == ZN_NO_PART) { return ISSET(item->flags, ZN_SENSITIVE_BIT); } return True; } /* ********************************************************************************** * * Pick -- * ********************************************************************************** */ static double Pick(ZnItem item, ZnPick ps) { TrackItem track = (TrackItem) item; ZnItem c_item; ZnBBox bbox; double dist=0, new_dist; ZnPoint *points, *p = ps->point; int num_points, i; int width=0, height=0; double width_2; int best_part; ZnPoint pts[2]; /* * Try one of the fields. */ dist = ZnFIELD.FieldsPick(&track->field_set, p, &best_part); if (dist <= 0.0) { goto report0; } /* * Try the current position symbol. */ ZnResetBBox(&bbox); if (track->symbol != ZnUnspecifiedImage) { ZnSizeOfImage(track->symbol, &width, &height); bbox.orig.x = track->dev.x-(width+1)/2; bbox.orig.y = track->dev.y-(height+1)/2; bbox.corner.x = bbox.orig.x + width; bbox.corner.y = bbox.orig.y + height; } new_dist = ZnRectangleToPointDist(&bbox, p); if (new_dist < dist) { best_part = CURRENT_POSITION; dist = new_dist; } if (dist <= 0.0) { goto report0; } /* * Try the leader. */ if (track->field_set.label_format && (track->leader_width > 0) && track->leader_points) { ZnPoint end_points[ZN_LINE_END_POINTS]; width_2 = (track->leader_width>1) ? ((double) track->leader_width)/2.0 : 0; points = (ZnPoint *) ZnListArray(track->leader_points); num_points = ZnListSize(track->leader_points)-1; for (i = 0; i < num_points; i++) { new_dist = ZnLineToPointDist(&points[i], &points[i+1], p, NULL); new_dist -= width_2; if (new_dist < dist) { best_part = LEADER; dist = new_dist; } if (dist <= 0.0) { goto report0; } } if (track->leader_first_end != NULL) { ZnGetLineEnd(&points[0], &points[1], track->leader_width, CapRound, track->leader_first_end, end_points); new_dist = ZnPolygonToPointDist(end_points, ZN_LINE_END_POINTS, p); if (new_dist < dist) { best_part = LEADER; dist = new_dist; } if (dist <= 0.0) { goto report0; } } if (track->leader_last_end != NULL) { ZnGetLineEnd(&points[num_points-1], &points[num_points-2], track->leader_width, CapRound, track->leader_last_end, end_points); new_dist = ZnPolygonToPointDist(end_points, ZN_LINE_END_POINTS, p); if (new_dist < dist) { best_part = LEADER; dist = new_dist; } if (dist <= 0.0) { goto report0; } } } /* * Try the speed vector. */ if ((item->class == ZnTrack) && (track->speed_vector_width > 0)) { pts[0] = track->dev; pts[1] = track->speed_vector_dev; new_dist = ZnPolylineToPointDist(pts, 2, track->speed_vector_width, CapRound, JoinRound, p); if (new_dist < dist) { best_part = SPEED_VECTOR; dist = new_dist; } if (dist <= 0.0) { goto report0; } } /* * Try the connection. */ c_item = item->connected_item; if ((c_item != ZN_NO_ITEM) && (track->connection_width > 0)) { pts[0] = track->dev; pts[1] = ((TrackItem) item->connected_item)->dev; new_dist = ZnPolylineToPointDist(pts, 2, track->connection_width, CapRound, JoinRound, p); if (new_dist < dist) { dist = new_dist; best_part = CONNECTION; } if (dist <= 0.0) { report0: dist = 0.0; } } //printf("track %d reporting part %d, distance %lf\n", item->id, best_part, dist); ps->a_part = best_part; return dist; } /* ********************************************************************************** * * PostScript -- * ********************************************************************************** */ static int PostScript(ZnItem item, ZnBool prepass, ZnBBox *area) { return TCL_OK; } #ifdef ATC /* ********************************************************************************** * * ZnSendTrackToOm -- * ********************************************************************************** */ /* * TODO: * * The tracks should be identified by their ids not their * structure pointer. This would enable an easy interface * between the overlap manager and the applications when * dealing with tracks. */ void * ZnSendTrackToOm(void *ptr, void *item, int *x, int *y, int *sv_dx, int *sv_dy, /*int *label_x, int *label_y, int *label_width, int *label_height,*/ int *rho, int *theta, int *visibility, int *locked, int *preferred_angle, int *convergence_style) { ZnWInfo *wi = (ZnWInfo *) ptr; ZnItem current_item = (ZnItem) item; TrackItem track; ZnBBox zn_bbox, bbox; ZnBool to_be_sent; int rho_derived ; zn_bbox.orig.x = zn_bbox.orig.y = 0; zn_bbox.corner.x = wi->width; zn_bbox.corner.y = wi->height; if (current_item == ZN_NO_ITEM) { current_item = ZnGroupHead(wi->om_group); } else { current_item = current_item->next; } while (current_item != ZN_NO_ITEM) { to_be_sent = current_item->class == ZnTrack; /* We send invisibles items because the current algorithm take care of the age of the tracks. to_be_sent &= ISSET(current_item->flags, ZN_VISIBLE_BIT);*/ ZnIntersectBBox(&zn_bbox, ¤t_item->item_bounding_box, &bbox); to_be_sent &= !ZnIsEmptyBBox(&bbox); if (to_be_sent) { track = (TrackItem) current_item; *x = (int) track->dev.x; *y = wi->height - ((int) track->dev.y); /* * We must send world values for speed vector deltas as device * equivalents can be null. But then if the image is rotated this * is nonsense. */ *sv_dx = (int) track->speed_vector.x; *sv_dy = (int) track->speed_vector.y; /* Fri Oct 13 15:16:38 2000 *label_x = track->field_set.label_pos.x; *label_y = wi->height - track->field_set.label_pos.y; if (track->field_set.label_format) { ZnDim bb_width, bb_height; ZnFIELD.GetLabelBBox(&track->field_set, &bb_width, &bb_height); *label_width = bb_width; *label_height = bb_height; } else { *label_width = 0; *label_height = 0; } */ /* * Trial to fix rho drift due to ZnPointPolarToCartesian * roundoff error. */ rho_derived = (int) sqrt(track->label_dx*track->label_dx + track->label_dy*track->label_dy); #ifdef DP if (ABS(rho_derived - track->label_distance) < LABEL_DISTANCE_THRESHOLD) { /* The error is narrow so value discarded */ *rho = track->label_distance ; } else { /* Means a user change has been performed on label_dx label_dy */ *rho = rho_derived ; } #else *rho = rho_derived; #endif *theta = track->label_angle; *visibility = (ISSET(current_item->flags, ZN_VISIBLE_BIT) ? 1 : 0 ); *locked = (ISSET(track->flags, FROZEN_LABEL_BIT) ? 1 : 0); *preferred_angle = track->label_preferred_angle; *convergence_style = track->label_convergence_style; break; } current_item = current_item->next; } return (void *) current_item; } /* ********************************************************************************** * * ZnSetLabelAngleFromOm -- * ********************************************************************************** */ void ZnSetLabelAngleFromOm(void *ptr, /* No longer in use. */ void *item, int rho, int theta, char *reason) /* Technical data explaining algorithm processing */ { TrackItem track = (TrackItem) item; theta %= 360; if (theta < 0) { theta += 360; } if (ISCLEAR(track->flags, FROZEN_LABEL_BIT) && (track->label_angle != theta)) { track->label_angle = theta; #ifdef DP track->label_distance = rho; #endif SET(track->flags, POLAR_BIT); ZnITEM.Invalidate((ZnItem) item, ZN_COORDS_FLAG); /* ZnGroupSetCallOm(((ZnItem)item)->parent, True);*/ } #ifdef OM_TECH /* Setup reason regardless of redrawing options. */ if (reason) { if (!track->omtechstuff) { track->omtechstuff = ZnMalloc( (OMTECHSTUFFLEN + 1) * sizeof(char)); } strncpy(track->omtechstuff, reason, OMTECHSTUFFLEN); } #endif } /* ********************************************************************************** * * ZnQueryLabelPosition -- OverlapMan query the widget about what would be the * label position if label_angle is theta * ********************************************************************************** */ void ZnQueryLabelPosition(void *ptr, /* No longer in use. */ void *item, int theta, int *x, int *y, int *w, int *h) { ZnItem it = (ZnItem) item; TrackItem track = (TrackItem) it; if (track->field_set.label_format) { ZnDim bb_width, bb_height; ZnPoint pos; ZnReal dx, dy; ZnTransfo t; ZnFIELD.GetLabelBBox(&track->field_set, &bb_width, &bb_height); ZnITEM.GetItemTransform(item, &t); AdjustLabelDistance(track, &t, theta, bb_width, bb_height, &pos, &dx, &dy); *x = (int) pos.x; *y = (int) (it->wi->height - pos.y); *w = (int) bb_width; *h = (int) bb_height; } else { *x = *y = *w = *h = 0; } } #endif /* ********************************************************************************** * * ZnSetHistoryVisibility -- PLC - not yet implemented * ********************************************************************************** */ void ZnSetHistoryVisibility(ZnItem item, int index, ZnBool visible) { } /* ********************************************************************************** * * ZnTruncHistory -- PLC - not yet interfaced * ********************************************************************************** */ void ZnTruncHistory(ZnItem item) { TrackItem track = (TrackItem) item; if (track->history) { int size = ZnListSize (track->history); History hist_tbl = ZnListArray (track->history); while (size--) { hist_tbl[size].visible = False; } ZnITEM.Invalidate(item, ZN_COORDS_FLAG); } } /* ********************************************************************************** * * GetFieldSet -- * ********************************************************************************** */ static ZnFieldSet GetFieldSet(ZnItem item) { return &((TrackItem) item)->field_set; } /* ********************************************************************************** * * GetAnchor -- * ********************************************************************************** */ static void GetAnchor(ZnItem item, Tk_Anchor anchor, ZnPoint *p) { ZnFieldSet field_set = &((TrackItem) item)->field_set; ZnDim width, height; if (field_set->label_format) { ZnFIELD.GetLabelBBox(field_set, &width, &height); ZnOrigin2Anchor(&field_set->label_pos, width, height, anchor, p); } else { p->x = p->y = 0.0; } } /* ********************************************************************************** * * Coords -- * Return or edit the item position. * ********************************************************************************** */ static int Coords(ZnItem item, int contour, int index, int cmd, ZnPoint **pts, char **controls, unsigned int *num_pts) { TrackItem track = (TrackItem) item; if ((cmd == ZN_COORDS_ADD) || (cmd == ZN_COORDS_ADD_LAST) || (cmd == ZN_COORDS_REMOVE)) { Tcl_AppendResult(item->wi->interp, " ", item->class->name, "s can't add or remove vertices", NULL); return TCL_ERROR; } else if ((cmd == ZN_COORDS_REPLACE) || (cmd == ZN_COORDS_REPLACE_ALL)) { if (*num_pts == 0) { Tcl_AppendResult(item->wi->interp, " coords command need 1 point on ", item->class->name, "s", NULL); return TCL_ERROR; } if (item->class == ZnTrack) { AddToHistory(track, track->pos); } track->pos = (*pts)[0]; ZnITEM.Invalidate(item, ZN_COORDS_FLAG); } else if ((cmd == ZN_COORDS_READ) || (cmd == ZN_COORDS_READ_ALL)) { *num_pts = 1; *pts = &track->pos; } return TCL_OK; } /* ********************************************************************************** * * Part -- * Convert a private part from/to symbolic representation. * ********************************************************************************** */ static int Part(ZnItem item, Tcl_Obj **part_spec, int *part) { char *part_str=""; int c; char *end; if (*part_spec) { part_str = Tcl_GetString(*part_spec); if (strlen(part_str) == 0) { *part = ZN_NO_PART; } else if (isdigit(part_str[0])) { *part = strtol(part_str, &end, 0); if ((*end != 0) || (*part < 0) || ((unsigned int) *part >= ((TrackItem) item)->field_set.num_fields)) { goto part_error; } } else { c = part_str[0]; if ((c == 'c') && (strcmp(part_str, "connection") == 0)) { *part = CONNECTION; } else if ((c == 'l') && (strcmp(part_str, "leader") == 0)) { *part = LEADER; } else if ((c == 'p') && (strcmp(part_str, "position") == 0)) { *part = CURRENT_POSITION; } else if ((c == 's') && (strcmp(part_str, "speedvector") == 0)) { if (item->class != ZnTrack) { goto part_error; } *part = SPEED_VECTOR; } else { part_error: Tcl_AppendResult(item->wi->interp, " invalid item part specification", NULL); return TCL_ERROR; } } } else { if (*part >= 0) { *part_spec = Tcl_NewIntObj(*part); } else { part_str = ""; switch (*part) { default: case ZN_NO_PART: break; case CURRENT_POSITION: part_str = "position"; break; case LEADER: part_str = "leader"; break; case CONNECTION: part_str = "connection"; break; case SPEED_VECTOR: if (item->class == ZnTrack) { part_str = "speedvector"; break; } } if (part_str[0]) { *part_spec = Tcl_NewStringObj(part_str, -1); } } } return TCL_OK; } /* ********************************************************************************** * * Index -- * Parse a text index and return its value and aa * error status (standard Tcl result). * ********************************************************************************** */ static int Index(ZnItem item, int field, Tcl_Obj *index_spec, int *index) { return ZnFIELD.FieldIndex(&((TrackItem) item)->field_set, field, index_spec, index); } /* ********************************************************************************** * * InsertChars -- * ********************************************************************************** */ static void InsertChars(ZnItem item, int field, int *index, char *chars) { if (ZnFIELD.FieldInsertChars(&((TrackItem) item)->field_set, field, index, chars)) { ZnITEM.Invalidate(item, ZN_COORDS_FLAG); } } /* ********************************************************************************** * * DeleteChars -- * ********************************************************************************** */ static void DeleteChars(ZnItem item, int field, int *first, int *last) { if (ZnFIELD.FieldDeleteChars(&((TrackItem) item)->field_set, field, first, last)) { ZnITEM.Invalidate(item, ZN_COORDS_FLAG); } } /* ********************************************************************************** * * Cursor -- * ********************************************************************************** */ static void TrackCursor(ZnItem item, int field, int index) { ZnFIELD.FieldCursor(&((TrackItem) item)->field_set, field, index); } /* ********************************************************************************** * * Selection -- * ********************************************************************************** */ static int Selection(ZnItem item, int field, int offset, char *chars, int max_chars) { return ZnFIELD.FieldSelection(&((TrackItem) item)->field_set, field, offset, chars, max_chars); } /* ********************************************************************************** * * Exported functions struct -- * ********************************************************************************** */ /* * Track -position attribute is not handled the same way as other * interface items like texts, icons and such, as it make little sense * to change the local transform of a track. It is always processed as * a point in the coordinate system of the track's parent. It is the same * for the points in the history and the speed vector end. */ static ZnItemClassStruct TRACK_ITEM_CLASS = { "track", sizeof(TrackItemStruct), track_attrs, 4, /* num_parts */ ZN_CLASS_HAS_ANCHORS|ZN_CLASS_ONE_COORD, /* flags */ -1, Init, Clone, Destroy, Configure, Query, GetFieldSet, GetAnchor, NULL, /* GetClipVertices */ NULL, /* GetContours */ Coords, InsertChars, DeleteChars, TrackCursor, Index, Part, Selection, NULL, /* Contour */ ComputeCoordinates, ToArea, Draw, Render, IsSensitive, Pick, NULL, /* PickVertex */ PostScript }; static ZnItemClassStruct WAY_POINT_ITEM_CLASS = { "waypoint", sizeof(TrackItemStruct), wp_attrs, 3, /* num_parts */ ZN_CLASS_HAS_ANCHORS|ZN_CLASS_ONE_COORD, /* flags */ -1, Init, Clone, Destroy, Configure, Query, GetFieldSet, GetAnchor, NULL, /* GetClipVertices */ NULL, /* GetContours */ Coords, InsertChars, DeleteChars, TrackCursor, Index, Part, Selection, NULL, /* Contour */ ComputeCoordinates, ToArea, Draw, Render, IsSensitive, Pick, NULL, /* PickVertex */ PostScript }; ZnItemClassId ZnTrack = (ZnItemClassId) &TRACK_ITEM_CLASS; ZnItemClassId ZnWayPoint = (ZnItemClassId) &WAY_POINT_ITEM_CLASS;