The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * Reticle.c -- Implementation of Reticle item.
 *
 * Authors              : Patrick Lecoanet.
 * Creation date        : Mon Feb  1 12:13:24 1999
 *
 * $Id: Reticle.c,v 1.45 2005/05/10 07:59:48 lecoanet Exp $
 */

/*
 *  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 "WidgetInfo.h"
#include "Item.h"
#include "Geo.h"
#include "Draw.h"

#include <math.h>


static const char rcsid[] = "$Id: Reticle.c,v 1.45 2005/05/10 07:59:48 lecoanet Exp $";
static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $";


/*
 * Draw as many circles as visible.
 */
#define ANY_CIRCLES     -1

/*
 * Some default values.
 */
#define DEFAULT_RETICLE_STEP_SIZE       80
#define DEFAULT_RETICLE_PERIOD          5

/*
 **********************************************************************************
 *
 * Specific Reticle item record
 *
 **********************************************************************************
 */

typedef struct _ReticleItemStruct {
  ZnItemStruct  header;
 
  /* Public data */
  ZnPoint       pos;                    /* Origin world coordinates     */
  ZnGradient    *line_color;            /* circle color           */
  ZnGradient    *bright_line_color;     /* intermediate circle color */
  ZnDim         first_radius;           /* first world radius             */
  ZnDim         step_size;              /* step world size                */
  int           period;                 /* bright circle period           */
  int           num_circles;            /* num cercles max                */
  ZnLineStyle   line_style;             /* circles lines styles           */
  ZnLineStyle   bright_line_style;

  /* Private data */
  ZnPoint       dev;                    /* item device coordinate         */
  ZnDim         first_radius_dev;       /* first device radius            */
  ZnDim         step_size_dev;          /* steps device size              */
} ReticleItemStruct, *ReticleItem;


static ZnAttrConfig     reticle_attrs[] = {
  { ZN_CONFIG_GRADIENT, "-brightlinecolor", NULL,
    Tk_Offset(ReticleItemStruct, bright_line_color), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_LINE_STYLE, "-brightlinestyle", NULL,
    Tk_Offset(ReticleItemStruct, bright_line_style), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_BOOL, "-composealpha", NULL,
    Tk_Offset(ReticleItemStruct, header.flags), ZN_COMPOSE_ALPHA_BIT,
    ZN_DRAW_FLAG, False },
  { ZN_CONFIG_BOOL, "-composerotation", NULL,
    Tk_Offset(ReticleItemStruct, header.flags), ZN_COMPOSE_ROTATION_BIT,
    ZN_COORDS_FLAG, False },
  { ZN_CONFIG_BOOL, "-composescale", NULL,
    Tk_Offset(ReticleItemStruct, header.flags), ZN_COMPOSE_SCALE_BIT,
    ZN_COORDS_FLAG, False },
  { ZN_CONFIG_DIM, "-stepsize", NULL,
    Tk_Offset(ReticleItemStruct, step_size), 0,
    ZN_COORDS_FLAG, False },
  { ZN_CONFIG_DIM, "-firstradius", NULL,
    Tk_Offset(ReticleItemStruct, first_radius), 0,
    ZN_COORDS_FLAG, False },
  { ZN_CONFIG_GRADIENT, "-linecolor", NULL,
    Tk_Offset(ReticleItemStruct, line_color), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_LINE_STYLE, "-linestyle", NULL,
    Tk_Offset(ReticleItemStruct, line_style), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_UINT, "-numcircles", NULL,
    Tk_Offset(ReticleItemStruct, num_circles), 0,
    ZN_COORDS_FLAG, False },
  { ZN_CONFIG_UINT, "-period", NULL,
    Tk_Offset(ReticleItemStruct, period), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_POINT, "-position", NULL,
    Tk_Offset(ReticleItemStruct, pos), 0,
    ZN_COORDS_FLAG, False},
  { ZN_CONFIG_PRI, "-priority", NULL,
    Tk_Offset(ReticleItemStruct, header.priority), 0,
    ZN_DRAW_FLAG|ZN_REPICK_FLAG, False },
  { ZN_CONFIG_BOOL, "-sensitive", NULL,
    Tk_Offset(ReticleItemStruct, header.flags), ZN_SENSITIVE_BIT,
    ZN_REPICK_FLAG, False },
  { ZN_CONFIG_TAG_LIST, "-tags", NULL,
    Tk_Offset(ReticleItemStruct, header.tags), 0, 0, False },
  { ZN_CONFIG_BOOL, "-visible", NULL,
    Tk_Offset(ReticleItemStruct, 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[])     
{
  ReticleItem   reticle = (ReticleItem) item;
  ZnWInfo       *wi = item->wi;
  
  SET(item->flags, ZN_VISIBLE_BIT);
  CLEAR(item->flags, ZN_SENSITIVE_BIT);
  SET(item->flags, ZN_COMPOSE_ALPHA_BIT);
  SET(item->flags, ZN_COMPOSE_ROTATION_BIT);
  SET(item->flags, ZN_COMPOSE_SCALE_BIT);
  item->priority = 0;
  item->part_sensitive = 0;
  reticle->line_color = ZnGetGradientByValue(wi->fore_color);
  reticle->bright_line_color = ZnGetGradientByValue(wi->fore_color);
  reticle->first_radius = DEFAULT_RETICLE_STEP_SIZE;
  reticle->step_size = DEFAULT_RETICLE_STEP_SIZE;
  reticle->period = DEFAULT_RETICLE_PERIOD;
  reticle->num_circles = ANY_CIRCLES;
  reticle->line_style = ZN_LINE_SIMPLE;
  reticle->bright_line_style = ZN_LINE_SIMPLE;
  reticle->pos.x = 0;
  reticle->pos.y = 0;
  reticle->dev.x = 0;
  reticle->dev.y = 0;
  reticle->first_radius_dev = 0;
  reticle->step_size_dev = 0;

  return TCL_OK;
}


/*
 **********************************************************************************
 *
 * Clone --
 *
 **********************************************************************************
 */
static void
Clone(ZnItem    item)
{
  ReticleItem   reticle = (ReticleItem) item;
  
  reticle->line_color = ZnGetGradientByValue(reticle->line_color);
  reticle->bright_line_color = ZnGetGradientByValue(reticle->bright_line_color);  
}


/*
 **********************************************************************************
 *
 * Destroy --
 *
 **********************************************************************************
 */
static void
Destroy(ZnItem  item)
{
  ReticleItem   reticle = (ReticleItem) item;
    
  ZnFreeGradient(reticle->line_color);
  ZnFreeGradient(reticle->bright_line_color);
}


/*
 **********************************************************************************
 *
 * Configure --
 *
 **********************************************************************************
 */
static int
Configure(ZnItem        item,
          int           argc,
          Tcl_Obj *CONST argv[],
          int           *flags)
{
  if (ZnConfigureAttributes(item->wi, item, item, reticle_attrs,
                            argc, argv, flags) == TCL_ERROR) {
    return TCL_ERROR;
  }

  return TCL_OK;
}


/*
 **********************************************************************************
 *
 * Query --
 *
 **********************************************************************************
 */
static int
Query(ZnItem            item,
      int               argc,
      Tcl_Obj *CONST    argv[])
{
  if (ZnQueryAttribute(item->wi->interp, item, reticle_attrs, argv[0]) == TCL_ERROR) {
    return TCL_ERROR;
  }  

  return TCL_OK;
}


/*
 **********************************************************************************
 *
 * ComputeCoordinates --
 *
 **********************************************************************************
 */
static void
ComputeCoordinates(ZnItem       item,
                   ZnBool       force)
{
  ZnWInfo       *wi = item->wi;
  ReticleItem   reticle  = (ReticleItem) item;
  ZnDim         half_width;
  ZnPoint       p, xp;
  
  /* Compute center device coordinates */
  p.x = p.y = 0;
  ZnTransformPoint(wi->current_transfo, &p, &reticle->dev);
  p.x = reticle->step_size;
  ZnTransformPoint(wi->current_transfo, &p, &xp);
  reticle->step_size_dev = hypot(xp.x - reticle->dev.x, xp.y - reticle->dev.y);
  p.x = reticle->first_radius;
  ZnTransformPoint(wi->current_transfo, &p, &xp);
  reticle->first_radius_dev = hypot(xp.x - reticle->dev.x, xp.y - reticle->dev.y);
  if (reticle->first_radius_dev < 1.0) {
    reticle->first_radius_dev = 1.0;
  }
  if (reticle->step_size_dev < 1.0) {
    reticle->step_size_dev = 1.0;
  }
  
  /* Reticle bounding box is zn bounding box or depends on num_circles */
  if (reticle->num_circles == ANY_CIRCLES) {
    item->item_bounding_box.orig.x = 0;
    item->item_bounding_box.orig.y = 0;
    item->item_bounding_box.corner.x = wi->width;
    item->item_bounding_box.corner.y = wi->height;
  }
  else {
    half_width = reticle->first_radius_dev +
                 (reticle->num_circles - 1) * reticle->step_size_dev;
    item->item_bounding_box.orig.x = reticle->dev.x - half_width;
    item->item_bounding_box.orig.y = reticle->dev.y - half_width;
    item->item_bounding_box.corner.x = item->item_bounding_box.orig.y + (2 * half_width);
    item->item_bounding_box.corner.y = item->item_bounding_box.orig.y + (2 * half_width);
  }
}


/*
 **********************************************************************************
 *
 * ToArea --
 *      Tell if the object is entirely outside (-1),
 *      entirely inside (1) or in between (0).
 *
 **********************************************************************************
 */
static int
ToArea(ZnItem   item,
       ZnToArea ta)
{
  return -1;
}


/*
 **********************************************************************************
 *
 * Draw --
 *
 **********************************************************************************
 */
static void
Draw(ZnItem     item)
{
  ZnWInfo       *wi = item->wi;
  ReticleItem   reticle = (ReticleItem) item;
  ZnDim         radius  = reticle->first_radius_dev;
  ZnDim         radius_max_dev;
  XGCValues     values;
  int           i;
  ZnDim         l1, l2, l3, l4;
/*  int         count = 0;*/

  /* Compute radius max */
  l1 = (ZnDim) hypot(wi->damaged_area.orig.x - reticle->dev.x,
                     wi->damaged_area.orig.y - reticle->dev.y);
  l2 = (ZnDim) hypot(wi->damaged_area.corner.x - reticle->dev.x,
                     wi->damaged_area.orig.y - reticle->dev.y);
  l3 = (ZnDim) hypot(wi->damaged_area.orig.x - reticle->dev.x,
                     wi->damaged_area.corner.y - reticle->dev.y);
  l4 = (ZnDim) hypot(wi->damaged_area.corner.x - reticle->dev.x,
                     wi->damaged_area.corner.y - reticle->dev.y);
  radius_max_dev = MAX(MAX(l1,l2), MAX(l3, l4));

  if (reticle->num_circles > 0) {
    radius_max_dev = MIN(radius_max_dev, reticle->first_radius_dev +
                         (reticle->num_circles - 1) * reticle->step_size_dev);
  }
  
  while (radius <= radius_max_dev) {
    ZnSetLineStyle(wi, reticle->line_style);
    values.foreground = ZnGetGradientPixel(reticle->line_color, 0.0);
    values.line_width = 0;
    values.fill_style = FillSolid;
    XChangeGC(wi->dpy, wi->gc, GCForeground | GCLineWidth | GCFillStyle, &values);
    for (i = 1; ((radius <= radius_max_dev) && (i < reticle->period)); i++) {
      if ((reticle->dev.x >= wi->damaged_area.orig.x - radius) &&
          (reticle->dev.x <= wi->damaged_area.corner.x + radius) &&
          (reticle->dev.y >= wi->damaged_area.orig.y - radius) &&
          (reticle->dev.y <= wi->damaged_area.corner.y + radius)) {
        XDrawArc(wi->dpy, wi->draw_buffer, wi->gc,
                 (int) (reticle->dev.x - radius),
                 (int) (reticle->dev.y - radius),
                 (unsigned int) (radius * 2 - 1),
                 (unsigned int) (radius * 2 - 1),
                 0, 360 * 64);
/*      count++;*/
      }
      radius += (reticle->step_size_dev);
    }
    if ((radius <= radius_max_dev) &&
        (reticle->dev.x >= wi->damaged_area.orig.x - radius) &&
        (reticle->dev.x <= wi->damaged_area.corner.x + radius) &&
        (reticle->dev.y >= wi->damaged_area.orig.y - radius) &&
        (reticle->dev.y <= wi->damaged_area.corner.y + radius)) {
      ZnSetLineStyle(wi, reticle->bright_line_style);
      values.foreground = ZnGetGradientPixel(reticle->bright_line_color, 0.0);
      values.line_width = 0;
      values.fill_style = FillSolid;
      XChangeGC(wi->dpy, wi->gc, GCForeground | GCLineWidth | GCFillStyle, &values);
      XDrawArc(wi->dpy, wi->draw_buffer, wi->gc,
               (int) (reticle->dev.x - radius),
               (int) (reticle->dev.y - radius),
               (unsigned int) (radius * 2 - 1),
               (unsigned int) (radius * 2 - 1),
               0, 360 * 64);
      /*count++;*/
    }
    radius += (reticle->step_size_dev);
  }
/*printf("# circles drawn: %d\n", count);*/
}


/*
 **********************************************************************************
 *
 * Render --
 *
 **********************************************************************************
 */
#ifdef GL
static void
Render(ZnItem   item)
{
  ZnWInfo       *wi = item->wi;
  ReticleItem   reticle = (ReticleItem) item;
  ZnDim         radius  = reticle->first_radius_dev;
  ZnDim         radius_max_dev, new, x, y, xo, yo;
  int           i, j;
  ZnPoint       *genarc;
  int           num_p;
  unsigned short alpha;
  XColor        *color;

  xo = reticle->dev.x;
  yo = reticle->dev.y;
  /* Compute radius max */
  radius_max_dev = 0;
  x = wi->damaged_area.orig.x - xo;
  y = wi->damaged_area.orig.y - yo;
  new = x*x + y*y;
  if (new > radius_max_dev) {
    radius_max_dev = new;
  }
  x = wi->damaged_area.corner.x - xo;
  y = wi->damaged_area.orig.y - yo;
  new = x*x + y*y;
  if (new > radius_max_dev) {
    radius_max_dev = new;
  }
  x = wi->damaged_area.orig.x - xo;
  y = wi->damaged_area.corner.y - yo;
  new = x*x + y*y;
  if (new > radius_max_dev) {
    radius_max_dev = new;
  }
  x = wi->damaged_area.corner.x - xo;
  y = wi->damaged_area.corner.y - yo;
  new = x*x + y*y;
  if (new > radius_max_dev) {
    radius_max_dev = new;
  }
  radius_max_dev = sqrt(radius_max_dev);

  if (reticle->num_circles > 0) {
    radius_max_dev = MIN(radius_max_dev, reticle->first_radius_dev +
                         (reticle->num_circles - 1) * reticle->step_size_dev);
  }

  genarc = ZnGetCirclePoints(3, ZN_CIRCLE_FINER, 0.0, 2*M_PI, &num_p, NULL);
  glLineWidth(1.0);
  while (radius <= radius_max_dev) {
    ZnSetLineStyle(wi, reticle->line_style);
    color = ZnGetGradientColor(reticle->line_color, 0.0, &alpha);
    alpha = ZnComposeAlpha(alpha, wi->alpha);
    glColor4us(color->red, color->green, color->blue, alpha);
    for (i = 1; ((radius <= radius_max_dev) && (i < reticle->period)); i++) {
      if ((xo >= wi->damaged_area.orig.x - radius) &&
          (xo <= wi->damaged_area.corner.x + radius) &&
          (yo >= wi->damaged_area.orig.y - radius) &&
          (yo <= wi->damaged_area.corner.y + radius)) {
        glBegin(GL_LINE_LOOP);
        for (j = 0; j < num_p; j++) {
          x = xo + genarc[j].x * radius;
          y = yo + genarc[j].y * radius;
          glVertex2d(x, y);
        }
        glEnd();
      }
      radius += (reticle->step_size_dev);
    }
    if ((radius <= radius_max_dev) &&
        (xo >= wi->damaged_area.orig.x - radius) &&
        (xo <= wi->damaged_area.corner.x + radius) &&
        (yo >= wi->damaged_area.orig.y - radius) &&
        (yo <= wi->damaged_area.corner.y + radius)) {
      ZnSetLineStyle(wi, reticle->bright_line_style);
      color = ZnGetGradientColor(reticle->bright_line_color, 0.0, &alpha);
      alpha = ZnComposeAlpha(alpha, wi->alpha);
      glColor4us(color->red, color->green, color->blue, alpha);
      glBegin(GL_LINE_LOOP);
      for (j = 0; j < num_p; j++) {
        x = xo + genarc[j].x * radius;
        y = yo + genarc[j].y * radius;
        glVertex2d(x, y);
      }
      glEnd();
    }
    radius += (reticle->step_size_dev);
  }
  glDisable(GL_LINE_STIPPLE);
}
#else
static void
Render(ZnItem   item)
{
}
#endif


/*
 **********************************************************************************
 *
 * IsSensitive --
 *
 **********************************************************************************
 */
static ZnBool
IsSensitive(ZnItem      item,
            int         item_part)
{
  return (ISSET(item->flags, ZN_SENSITIVE_BIT) &&
          item->parent->class->IsSensitive(item->parent, ZN_NO_PART));
}


/*
 **********************************************************************************
 *
 * Pick --
 *      Nothing to pick, we are almost transparent.
 *
 **********************************************************************************
 */
static double
Pick(ZnItem     item,
     ZnPick     ps)
{
  return 1e40;
}


/*
 **********************************************************************************
 *
 * Coords --
 *      Return or edit the item center.
 *
 **********************************************************************************
 */
static int
Coords(ZnItem           item,
       int              contour,
       int              index,
       int              cmd,
       ZnPoint          **pts,
       char             **controls,
       unsigned int     *num_pts)
{
  ReticleItem   reticle = (ReticleItem) item;
  
  if ((cmd == ZN_COORDS_ADD) || (cmd == ZN_COORDS_ADD_LAST) || (cmd == ZN_COORDS_REMOVE)) {
    Tcl_AppendResult(item->wi->interp,
                     " reticles 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 reticles", NULL);
      return TCL_ERROR;
    }
    reticle->pos = (*pts)[0];
    ZnITEM.Invalidate(item, ZN_COORDS_FLAG);
  }
  else if ((cmd == ZN_COORDS_READ) || (cmd == ZN_COORDS_READ_ALL)) {
    *num_pts = 1;
    *pts = &reticle->pos;
  }
  return TCL_OK;
}


/*
 **********************************************************************************
 *
 * PostScript --
 *
 **********************************************************************************
 */
static int
PostScript(ZnItem item,
           ZnBool prepass,
           ZnBBox *area)
{
  return TCL_OK;
}


/*
 **********************************************************************************
 *
 * Exported functions struct --
 *
 **********************************************************************************
 */
static ZnItemClassStruct RETICLE_ITEM_CLASS = {
  "reticle",
  sizeof(ReticleItemStruct),
  reticle_attrs,
  0,                    /* num_parts */
  ZN_CLASS_ONE_COORD,   /* flags */
  Tk_Offset(ReticleItemStruct, pos),
  Init,
  Clone,
  Destroy,
  Configure,
  Query,
  NULL,                 /* GetFieldSet */
  NULL,                 /* GetAnchor */
  NULL,                 /* GetClipVertices */
  NULL,                 /* GetContours */
  Coords,
  NULL,                 /* InsertChars */
  NULL,                 /* DeleteChars */
  NULL,                 /* Cursor */
  NULL,                 /* Index */
  NULL,                 /* Part */
  NULL,                 /* Selection */
  NULL,                 /* Contour */
  ComputeCoordinates,
  ToArea,
  Draw,
  Render,
  IsSensitive,
  Pick,
  NULL,                 /* PickVertex */
  PostScript
};

ZnItemClassId ZnReticle = (ZnItemClassId) &RETICLE_ITEM_CLASS;