/* code to control image processing operations. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xephem.h" #include "fsmatch.h" void si_off (void); static Pixel gray_pixel (int fp); static Widget addPB (Widget rc_w, char *label, int align); static Widget addTF (Widget rc_w, char *name, int rw); static void addLabel (Widget rc_w, char *label, int align); static void build_colormap (void); static void build_histo (void); static void chooseMag (int sw, int sh, int lr, int tb, ZM_Undo *zp, int nz); static void glassSetup (void); static void glassSize (void); static void si_ne (void); static void si_createdialog (void); static void si_createwcsdialog (void); static void sectionCtrl (Widget par_w, char *hlptag, Widget *tb, char *tbname, char *tblabel, char *tbtip); static void si_wcsuse_cb (Widget w, XtPointer client, XtPointer call); static void si_markstars_cb (Widget w, XtPointer client, XtPointer call); static void si_lohi_cb (Widget w, XtPointer client, XtPointer call); static void si_wcsgo_cb (Widget w, XtPointer client, XtPointer call); static void si_wcsclose_cb (Widget w, XtPointer client, XtPointer call); static void si_close_cb (Widget w, XtPointer client, XtPointer call); static void si_exp_cb (Widget w, XtPointer client, XtPointer call); static void si_drawHistogram (void); static void si_gamma_cb (Widget w, XtPointer client, XtPointer call); static void si_help_cb (Widget w, XtPointer client, XtPointer call); static void si_wcshelp_cb (Widget w, XtPointer client, XtPointer call); static void si_inv_cb (Widget w, XtPointer client, XtPointer call); static void si_contrast_cb (Widget w, XtPointer client, XtPointer call); static void si_markrefstar_cb (Widget w, XtPointer client, XtPointer call); static void si_newrefstar_cb (Widget w, XtPointer client, XtPointer call); static void si_newref_cb (Widget w, XtPointer client, XtPointer call); static void si_managetb_cb (Widget w, XtPointer client, XtPointer call); static void si_newImage (char *name, int autocon); static void printGlassStats (ImRegion *rp, double cimx, double cimy); static void printImageStats (char *name); static void drawSlice (int x0, int y0, int ww, int wh, int x1, int y1, int lr, int tb); static void drawGraphGrid (Display *dsp, Window win, int ww, int wh, double xmin, double max, double ymin, double ymax); static void drawGlassRow (Widget w, ImRegion *rp, int rx, int rw); static void drawGlassCol (Widget w, ImRegion *rp, int ry, int rh); static void defContrast (void); static void si_wide (void); static void si_narrow (void); static void si_full (void); static void si_duller (void); static void si_sharper (void); static void si_brighter (void); static void si_darker (void); static void si_mcontrast (void); static void si_motion_eh (Widget w, XtPointer client, XEvent *ev, Boolean *continue_to_dispatch); static void mkGCs (void); static void wcsMatch (void); static int initSolverFields (void); static int glimsz (void); static void findWholeImageStats (void); static void matchStats (ObjF *fsp, double *sx, double *sy, int nfs); static void imPhotom (double ix, double iy); static void makeGlassImage (void); static void fillGlass (int wx, int wy); static Widget si_w; /* main imaging tools dialog */ static Widget wcs_w; /* main wcs dialog */ static Widget fda_w; /* Drawing area for the contrast map */ static Widget sda_w; /* Drawing area for slice cross sections */ static Widget glrda_w; /* Drawing area for glass row plot */ static Widget glcda_w; /* Drawing area for glass column plot */ static Widget gamma_w; /* gamma scale */ static Widget ctb_w; /* TB for contrast section */ static Widget ptb_w; /* TB for gaussian section */ static Widget gtb_w; /* TB for glass Stats section */ static Widget stb_w; /* TB for Slice section */ static Widget rtb_w; /* TB for ROI section */ static Widget ostxt_w; /* R/O Text for overall image stats */ static Widget phtxt_w; /* R/O Text for photometric stats */ static Widget phmagref_w; /* R/W TF for photometric magnitude reference */ static Widget glcol_w; /* TB whether to draw vertical cross section */ static Widget glrow_w; /* TB whether to draw horizontl cross section */ static Widget lo_w; /* lower contrast setting */ static Widget hi_w; /* higher contrast setting */ static Widget rstxt_w; /* Text for ROI stats */ static Widget burnt_w; /* TF for specifying burned out pix */ static Widget snr_w; /* TF for specifying star snr */ static Widget bsep_w; /* TF for best wcs separation */ static Widget wsep_w; /* TF for worst wcs separation */ static Widget inv_w; /* TB for reverse vid */ static Widget slmax_w; /* TB slice auto contrast */ typedef enum { WCS_RA, WCS_DEC, WCS_RSCALE, WCS_USCALE, WCS_ROT, WCS_N } WCSFName; /* callback code */ typedef struct { WCSFName which; /* field name code to allow for random access */ char *label; /* field label */ char *kwtip; /* help tip for keyword TF */ char *vtip; /* help tip for value TF */ char *res; /* instance name of TF with FITS fields */ Widget kw_w; /* FITS keyword TF */ Widget v_w; /* value TF */ double v; /* handy value */ } WCSSeed; static WCSSeed wcsseed[WCS_N] = { {WCS_RA, "RA, H:M:S", "FITS field for Center RA", "Center RA", "RA"}, {WCS_DEC, "Dec, D:M:S", "FITS field for Center Dec","Center Dec","Dec"}, {WCS_RSCALE, "°/Pixel right", "FITS field for sky step right", "Sky angle right, degrees per pixel", "StepRight"}, {WCS_USCALE, "°/Pixel up", "FITS field for sky step up", "Sky angle up, degrees per pixel", "StepUp"}, {WCS_ROT, "Rotate °EofN", "FITS field for rotation", "Image rotation", "Rotation"}, }; static int setWCSField (WCSSeed *wsp, char msg[]); static int getWCSSeed (WCSSeed *wsp, double *vp); static FImage fim; /* current FITS image */ static Pixmap fpm; /* current pixmap with image */ static int fpmw, fpmh; /* dimensions of fpm */ static int fimok; /* 1 if fim is ok, else 0 */ static Pixel *gray; /* gray-scale ramp for drawing image */ static int ngray; /* number of pixels usable in gray[] */ static int fdepth; /* depth of image, in bits */ static int fbpp; /* bits per pixel in image: 1, 8, 16 or 32 */ static double fmag; /* screen pix / image pix */ static double fximoff, fyimoff; /* image offsets that put center on screen */ static int lopix, hipix; /* current LUT lo and hi image pixel values */ static int *histo; /* malloced histogram of current image */ static int h_mini, h_maxi; /* histo[] index to first and last pixel used */ static int h_peaki; /* index of largest value in histo[] */ static char *colormap; /* malloced gray[] indeces for current image */ static int want_inv; /* set when want inverse video effect */ static int nup; /* set if fim has north up */ static int elf; /* set if fim has east left */ static ImStats wims; /* handy stats for whole image */ static Pixel imbg_p; /* grid background color */ static Pixel img_p; /* grid color 1 */ static Pixel imc1_p; /* handy color 1 */ static Pixel imc2_p; /* handy color 2 */ static Pixel imc3_p; /* handy color 3 */ static GC imgc; /* handy GC */ #define MMH 10 /* map marker height */ #define MMW 5 /* map marker half-width */ #define MONUHI 30 /* height of tallest monument, pixels */ #define GAM_MAX 3.0 /* gamma ranges from 1/GAM_MAX .. GAM_MAX */ #define DEFGAMMA 75 /* default gamma (in scale's range) */ #define NTICKS 5 /* rough number of tick marks in plots */ #define SINDENT 15 /* main section indent */ #define FOVMORE 1.1 /* size factor over diagonal for wcs fit */ #define BORDER 8 /* edge to ignore looking for stars */ static XImage *glass_xim; /* glass XImage -- 0 means new or can't */ static GC glassGC; /* GC for glass border */ static Widget glstxt_w; /* Text for glass stats */ typedef struct { char *name; /* TB name for resource */ char *label; /* toggle button label */ int value; /* numeric value to use when selected */ Widget w; /* TB widget in radio box */ } RadioSet; static RadioSet glmag[] = { {"1x", " 1x", 1}, {"2x", " 2x", 2}, {"4x", " 4x", 4}, {"8x", " 8x", 8}, }; static RadioSet glsz[] = { {"16x16", " 16", 16}, {"32x32", " 32", 32}, {"64x64", " 64", 64}, {"128x128", "128", 128}, }; static char lopixkw[] = "XELOGLUT"; /* our FITS hw for lopix */ static char hipixkw[] = "XEHIGLUT"; /* our FITS hw for hipix */ static char gammakw[] = "XEGAMMA"; /* our FITS hw for gamma */ #define MINGLSZ 3 /* anything smaller causes several grief */ static int glassmag; /* glass mag factor, any integer > 0 */ static int glasssz; /* glass edge, screen pixels, int X of mag */ static Star newstar; /* current photometric computation */ static Star refstar; /* photometric reference */ static int refstarok; /* set when refstar is valid */ static int newstarok; /* set when newstar is valid */ static char skyipcategory[] = "Sky View -- Image tools";/* Save category */ /* called to create but not manage the imaging dialog */ void si_create() { if (!si_w) { si_createdialog(); si_createwcsdialog(); mkGCs(); ngray = gray_ramp (XtD, xe_cm, &gray); fdepth = DefaultDepth (XtD, DefaultScreen(XtD)); fbpp = (fdepth == 1 || ngray == 2) ? 1 : (fdepth>=17 ? 32 : (fdepth >= 9 ? 16 : 8)); glassSetup(); /* insure glassmag/sz set */ } } /* called to bring up the imaging dialog */ void si_manage() { si_create(); XtManageChild(si_w); } /* called to find whether Imaging window is up */ int si_isup() { return (isUp (si_w)); } /* called to unmanage the imaging dialog. * N.B. do not try to reclaim memory so we can leave FITS watch on while down. */ void si_unmanage() { if (!si_w) return; XtUnmanageChild (si_w); } /* return 1 if dialog is up, else 0. */ int si_ismanaged() { return (si_w && XtIsManaged(si_w)); } /* return 1 if there is currently a valid FITS image available, else 0 */ int si_ison() { return (fimok); } /* discard any current FITS image */ void si_off() { if (fimok) { resetFImage (&fim); fimok = 0; } if (fpm) { XFreePixmap (XtD, fpm); fpm = (Pixmap)0; } } /* set the object at the given image coords as the new reference object, * assigned with magnitude mag. */ void si_setPhotomRef (ix, iy, mag) double ix, iy, mag; { char magstr[32]; /* install new ref mag number */ sprintf (magstr, "%.2f", mag); XmTextFieldSetString (phmagref_w, magstr); /* force new refstar, compute and mark */ refstarok = 0; imPhotom (ix, iy); sv_all(NULL); /* erase */ sv_drawimdot (ix, iy, 5, 0); } /* called when the Gauss toolbar TB is used. * N.B. can be called before we are created */ void si_updateGauss (on) int on; { if (fimok && on) si_manage(); else si_create(); XmToggleButtonSetState (ptb_w, on, True); } /* called when the Glass toolbar TB is used. * N.B. can be called before we are created */ void si_updateGlass (on) int on; { /* always close, open only if window already up */ si_create(); if (!on || si_isup()) XmToggleButtonSetState (gtb_w, on, True); } /* called when the ROI toolbar TB is used. * N.B. can be called before we are created */ void si_updateROI (on) int on; { /* always close, open only if window already up */ si_create(); if (!on || (si_isup() && si_ison())) XmToggleButtonSetState (rtb_w, on, True); } /* called when the Contrast toolbar TB is used. * N.B. can be called before we are created */ void si_updateContrast (on) int on; { if (fimok && on) si_manage(); else si_create(); XmToggleButtonSetState (ctb_w, on, True); } /* called when the Slice toolbar TB is used. * N.B. can be called before we are created */ void si_updateSlice (on) int on; { if (fimok && on) si_manage(); else si_create(); XmToggleButtonSetState (stb_w, on, True); } /* called to put up or remove the watch cursor. * we grab the WCS window while we're at it. */ void si_cursor (c) Cursor c; { Window win; if (si_w && (win = XtWindow(si_w)) != 0) { Display *dsp = XtDisplay(si_w); if (c) XDefineCursor (dsp, win, c); else XUndefineCursor (dsp, win); } if (wcs_w && (win = XtWindow(wcs_w)) != 0) { Display *dsp = XtDisplay(wcs_w); if (c) XDefineCursor (dsp, win, c); else XUndefineCursor (dsp, win); } } /* return pointer to the current FImage. * if no current FImage, return NULL. */ FImage * si_getFImage () { return (fimok ? &fim : NULL); } /* set the contrast fields in the given FITS header */ void si_setContrast (fip) FImage *fip; { int lpix, hpix; int gamma; /* lo>hi will imply reverse video */ if (want_inv) { lpix = hipix; hpix = lopix; } else { lpix = lopix; hpix = hipix; } setIntFITS (fip, lopixkw, lpix, "XEphem: Pixel at low LUT end"); setIntFITS (fip, hipixkw, hpix, "XEphem: Pixel at hi LUT end"); XmScaleGetValue (gamma_w, &gamma); setRealFITS (fip, gammakw, gamma/100., 4, "XEphem: LUT gamma value"); sf_showHeader(fip); } /* return the current Pixmap */ Pixmap si_getPixmap () { return (fpm); } /* send the current image to the postscript machine */ void si_ps () { XPSPixmap (fpm, fpmw, fpmh, xe_cm, 0, 0); } /* build a pixmap, fpm, from the XImage fim with the given size, flipping and * zoom inf, using the current colormap. */ void si_newPixmap (w, h, lr, tb, zp, nz) int w, h; /* screen size */ int lr, tb; /* display's flipping */ ZM_Undo *zp; /* entire current zoom stack */ int nz; /* items on zoom stack */ { Display *dsp = XtDisplay (toplevel_w); Window win = XtWindow (toplevel_w); GC gc = DefaultGC(dsp, DefaultScreen(dsp)); CamPix *ip = (CamPix *)fim.image; /* just handy */ XImage *f_xim; /* XImage: adjusted for flipping and lo/hi */ char *data; /* pixel data for f_xim */ int nbytes; /* total bytes in data[] */ int x, y; /* screen coords */ int wantmonu; /* whether we want monument mode */ int dpix; /* h_maxi - h_mini */ if (!fimok) return; watch_cursor (1); /* create a temp XImage in which to build image for pixmap */ /* get memory for image pixels */ nbytes = (h+7)*(w+7)*fbpp/8; data = (char *) malloc (nbytes); if (!data) { xe_msg (1, "Can not get %d bytes for FITS pixels", nbytes); watch_cursor (0); return; } /* create the XImage */ f_xim = XCreateImage (dsp, XDefaultVisual (dsp, DefaultScreen(dsp)), /* depth */ fbpp == 1 ? 1 : fdepth, /* format */ fbpp == 1 ? XYBitmap : ZPixmap, /* offset */ 0, /* data */ data, /* width */ w, /* height */ h, /* pad */ fbpp < 8 ? 8 : fbpp, /* bpl */ 0); if (!f_xim) { xe_msg (1, "Can not create shadow XImage"); free ((void *)data); watch_cursor (0); return; } f_xim->bitmap_bit_order = LSBFirst; f_xim->byte_order = LSBFirst; /* decide magnification and offsets */ chooseMag (w, h, lr, tb, zp, nz); /* fill image via gray map and allow for flipping, centering, monument. * use fast/clean nearest-neighbor interpolation. */ if (!elf) lr = !lr; if (!nup) tb = !tb; dpix = h_maxi - h_mini; wantmonu = svtb_monumentIsOn() && dpix > 0; for (y = 0; y < h; y++) { int flipy = tb ? h-y-1 : y; CamPix *rowp = &ip[fim.sw*((int)(flipy/fmag+fyimoff+.5))]; for (x = 0; x < w; x++) { int flipx = lr ? w-x-1 : x; int p = rowp[(int)(flipx/fmag+fximoff+.5)]; Pixel xp = gray_pixel (p); XPutPixel (f_xim, x, y, xp); if (wantmonu) { int z, zmax = (int)(MONUHI*fmag*(p-h_mini)/dpix); for (z = 1; z < zmax && z <= y; z++) XPutPixel (f_xim, x, y-z, xp); } } } /* (re)create the pixmap if first or new size */ if (!fpm || w != fpmw || h != fpmh) { fpm = XCreatePixmap (dsp, win, w, h, fdepth); fpmw = w; fpmh = h; } /* copy the image to the pixmap */ XPutImage (dsp, fpm, gc, f_xim, 0, 0, 0, 0, w, h); /* finished with the image */ free ((void *)f_xim->data); f_xim->data = NULL; XDestroyImage (f_xim); watch_cursor (0); } /* install fip as the new fim and display. * N.B. malloced fields in fip are just re-used, so do not reset it on return. * name might include leading path, if so we strip off. * last argument determines whether contrast and WCS are set automatically. */ void si_newfim (fip, name, autocon) FImage *fip; char *name; int autocon; { double eq; /* we support 8 bit images by converting them to 16 */ if (fip->bitpix == 8) { /* convert to 16 bits. * thanks to Egil Kvaleberg totbytes * 2; unsigned char *new_image = (unsigned char *) realloc (fip->image, new_bytes); if (!new_image) { xe_msg (1, "Could not realloc %d for pixels", new_bytes); resetFImage (fip); return; } /* convert from BITPIX 8 to BITPIX 16 */ for (n=fip->totbytes-1; n>=0; --n) ((unsigned short *)new_image)[n] = 256*new_image[n]; /* update FITS header to reflect BITPIX 16 */ fip->totbytes = new_bytes; fip->image = (char *) new_image; fip->bitpix = 16; setIntFITS (fip, "BITPIX", 16, "Bits per pixel"); } /* EQUINOX is preferred, but we'll accept EPOCH in a pinch */ if (getRealFITS (fip, "EQUINOX", &eq) < 0) { if (getRealFITS (fip, "EPOCH", &eq) < 0) { setRealFITS (fip, "EQUINOX", 2000.0, 10, "Faked"); } else { setRealFITS (fip, "EQUINOX", eq, 10, "Copied from EPOCH"); } } resetFImage (&fim); (void) memcpy ((void *)&fim, (void *)fip, sizeof(fim)); fimok = 1; si_newImage(name, autocon); } /* convert FITS x/y (well, 0-based "FITS") to X Windows x/y. * this always assumes you want east-left and north-up */ void si_im2win ( double imx, double imy, /* FITS image coords */ int winw, int winh, /* X window w and h */ int *winxp, int *winyp) /* return X window x and y */ { *winxp = (int)floor(fmag*(imx-fximoff) + .5); *winyp = (int)floor(fmag*(imy-fyimoff) + .5); if (!elf) *winxp = winw - 1 - *winxp; if (!nup) *winyp = winh - 1 - *winyp; } /* convert X Windows x/y to FITS x/y (well, 0-based "FITS"). * this always assumes you want east-left and north-up */ void si_win2im ( int winx, int winy, /* X window x and y */ int winw, int winh, /* X window w and h */ double *imxp, double *imyp) /* return FITS image coords */ { if (!elf) winx = winw - 1 - winx; if (!nup) winy = winh - 1 - winy; *imxp = winx/fmag+fximoff; *imyp = winy/fmag+fyimoff; } /* get the given WCS seed value from the GUI. * return 0 if ok, else fuss with xe_msg and return -1 */ static int getWCSSeed (wsp, vp) WCSSeed *wsp; double *vp; { double v; char *str; int ok; /* read TF */ str = XmTextFieldGetString (wsp->v_w); ok = f_scansexa (str, &v); XtFree (str); if (ok < 0) { xe_msg (1, "Missing or ill-formed format for %s", wsp->label); return (-1); } /* a few special cases */ switch (wsp->which) { case WCS_RA: *vp = hrrad(v); return(0); case WCS_RSCALE: /* FALLTHRU */ case WCS_USCALE: if (v == 0) { xe_msg (1, "Missing or 0 for %s", wsp->label); return (-1); } break; default: break; } *vp = degrad(v); return (0); } /* set global fmag/fximoff/fyimoff for given window and image portion */ static void setMag (ww, wh, imx, imy, imw, imh) int ww, wh; double imx, imy, imw, imh; { /* set mag and offsets to display desired portion */ fmag = wh/imh; if (fmag*imw < ww) { /* full width, center in height */ fmag = ww/imw; fximoff = imx - 0.5; fyimoff = imy + (imh - wh/fmag)/2 - 0.5; } else { /* full height, center in width */ fximoff = imx + (imw - ww/fmag)/2 - 0.5; fyimoff = imy - 0.5; } } /* find a magnification and image offsets to center image such that we always * fill the screen, centering and cropping if necessary. * in order to establish zoom current context, go through all entries in order. */ static void chooseMag (ww, wh, lr, tb, zp, nz) int ww, wh; /* window w/h */ int lr, tb; /* user's notion of what is flipped */ ZM_Undo *zp; /* zoom info in screen coords, else NULL */ int nz; /* n zoom entries */ { double imx, imy; /* corner of image to display, im pixels */ double imw, imh; /* size of image to display, im pixels */ int fx, fy; /* flipped coords */ /* start with no zoom, then work through stack */ imw = fim.sw; imh = fim.sh; imx = 0; imy = 0; setMag (ww, wh, imx, imy, imw, imh); for (; nz--; zp++) { /* display cropped portion */ double ix0, iy0; /* one corner of image */ double ix1, iy1; /* other corner of image */ fx = lr ? ww - 1 - zp->x0 : zp->x0; fy = tb ? wh - 1 - zp->y0 : zp->y0; si_win2im (fx, fy, ww, wh, &ix0, &iy0); fx = lr ? ww - 1 - zp->x1 : zp->x1; fy = tb ? wh - 1 - zp->y1 : zp->y1; si_win2im (fx, fy, ww, wh, &ix1, &iy1); imw = fabs(ix1-ix0); imh = fabs(iy1-iy0); imx = ix0 < ix1 ? ix0 : ix1; imy = iy0 < iy1 ? iy0 : iy1; setMag (ww, wh, imx, imy, imw, imh); } } /* compute the histogram for fim and put it in histo[]. */ static void build_histo() { FImage *fip = &fim; CamPix *ip = (CamPix *)fip->image; int npix = fip->sw*fip->sh; int i; /* insure memory */ if (!histo) { histo = (int *) malloc (MAXCAMPIX * sizeof(histo[0])); if (!histo) { printf ("No memory for histogram\n"); abort(); } } /* zero the histogram */ memset ((void *)histo, 0, MAXCAMPIX*sizeof(histo[0])); /* compute histogram */ for (i = 0; i < npix; i++) histo[(int)(*ip++)]++; /* find boundary stats */ for (h_mini = 0; histo[h_mini] == 0; h_mini++) continue; for (h_maxi = MAXCAMPIX; histo[--h_maxi] == 0; ) continue; if (h_mini == h_maxi) { if (h_mini > 0) h_mini -= 1; else h_maxi += 1; } h_peaki = 0; for (i = h_mini; i <= h_maxi; i++) if (histo[i] > histo[h_peaki]) h_peaki = i; } /* compute the colormap for fim and put in colormap, allowing for method * and lo/hi. * N.B. if we need the histogram, we assume it's already built. */ static void build_colormap() { int range = hipix - lopix; int g, i; /* insure memory */ if (!colormap) { colormap = malloc (MAXCAMPIX*sizeof(colormap[0])); if (!colormap) { printf ("No memory for colormap\n"); abort(); } } /* build ramp for current gamma setting */ for (i = 0; i < lopix; i++) colormap[i] = 0; XmScaleGetValue (gamma_w, &g); for (; i < hipix; i++) colormap[i] = (int)(pow((double)(i-lopix)/range, g/100.0)*ngray); for (; i < MAXCAMPIX; i++) colormap[i] = ngray-1; } /* given a FITS pixel, return an X pixel */ static Pixel gray_pixel (fp) int fp; { int gp = (int)colormap[fp]; if (want_inv) gp = (ngray-1) - gp; return (gray[gp]); } /* walk gradient to brightest pixel. * no use using true max because of errors introduced changing from win, to im * to win to im coords (here and in, say, computing the glass stats). */ void si_findSnap ( int ww, int wh, /* size of X Window */ int wx, int wy, /* X window coords of cursor */ int lr, int tb, /* user's flip settings */ int *sxp, int *syp) /* snap coords */ { ImRegion imr; double ix, iy; int fx, fy; int bx, by; /* null case if no image */ if (!fimok) { *sxp = wx; *syp = wy; return; } /* find image coord of cursor */ fx = lr ? ww - 1 - wx : wx; fy = tb ? wh - 1 - wy : wy; si_win2im (fx, fy, ww, wh, &ix, &iy); /* walk up the hill */ imr.im = (CamPix *) fim.image; imr.iw = fim.sw; imr.ih = fim.sh; imr.rx = (int)(ix + .5); imr.ry = (int)(iy + .5); imr.rw = 1; imr.rh = 1; brightWalk (&imr, NULL, &bx, &by); /* turn back into X cursor coords */ si_im2win ((double)bx, (double)by, ww, wh, &fx, &fy); *sxp = lr ? ww - 1 - fx : fx; *syp = tb ? wh - 1 - fy : fy; } /* handle the operation of the magnifying glass. */ void si_doGlass ( Display *dsp, Window win, int b1p, int m1, /* button/motion state */ int ww, int wh, /* size of X Window */ int wx, int wy, /* true X window coords of cursor */ int sx, int sy, /* snap-to-max X window coords */ int lr, int tb) /* user's flip settings */ { static int lastsx, lastsy; /* last windos x/y */ int rx, ry, rw, rh; /* region */ if (!fimok) return; if (b1p) glassSetup(); if (m1) { /* motion: put back old pixels that won't just be covered again */ /* first the vertical strip that is uncovered */ rh = glasssz; ry = lastsy - (glasssz/2); if (ry < 0) { rh += ry; ry = 0; } if (sx < lastsx) { rw = lastsx - sx; /* cursor moved left */ rx = sx + (glasssz/2); } else { rw = sx - lastsx; /* cursor moved right */ rx = lastsx - (glasssz/2); } if (rx < 0) { rw += rx; rx = 0; } if (rw > 0 && rh > 0) XCopyArea (dsp, fpm, win, glassGC, rx, ry, rw, rh, rx, ry); /* then the horizontal strip that is uncovered */ rw = glasssz; rx = lastsx - (glasssz/2); if (rx < 0) { rw += rx; rx = 0; } if (sy < lastsy) { rh = lastsy - sy; /* cursor moved up */ ry = sy + (glasssz/2); } else { rh = sy - lastsy; /* cursor moved dosn */ ry = lastsy - (glasssz/2); } if (ry < 0) { rh += ry; ry = 0; } if (rw > 0 && rh > 0) XCopyArea (dsp, fpm, win, glassGC, rx, ry, rw, rh, rx, ry); } if (b1p || m1) { /* start or new location: show glass and save new location */ fillGlass (sx, sy); XPutImage (dsp, win, glassGC, glass_xim, 0, 0, sx-(glasssz/2), sy-(glasssz/2), glasssz, glasssz); lastsx = sx; lastsy = sy; /* kinda hard to tell boundry of glass so draw a line around it */ XDrawRectangle (dsp, win, glassGC, sx-(glasssz/2), sy-(glasssz/2), glasssz-1, glasssz-1); /* show stats and graphs as desired */ if (svtb_glassIsOn() && gtb_w && XmToggleButtonGetState(gtb_w)) { int fx = lr ? ww - 1 - sx : sx; int fy = tb ? wh - 1 - sy : sy; int gsz = glimsz(); ImRegion imr; int cx, cy; double gix, giy; /* find glass region */ si_win2im (fx, fy, ww, wh, &gix, &giy); cx = (int)(gix + .5); cy = (int)(giy + .5); imr.im = (CamPix *) fim.image; imr.iw = fim.sw; imr.ih = fim.sh; imr.rx = cx - gsz/2; imr.ry = cy - gsz/2; imr.rw = gsz; imr.rh = gsz; if (clampRegion (&imr) < 0) return; /* print stats */ printGlassStats(&imr, gix, giy); /* plot desired crosssection through center of glass */ if (XmToggleButtonGetState (glrow_w)) drawGlassRow (glrda_w, &imr, cx - gsz/2, gsz); if (XmToggleButtonGetState (glcol_w)) drawGlassCol (glcda_w, &imr, cy - gsz/2, gsz); } } } /* print stats for glass given image location of center */ static void printGlassStats(rp, ix, iy) ImRegion *rp; double ix, iy; { ImStats ims; int cx, cy; char buf[1024]; regionStats (rp, &ims); cx = (int)(ix + .5); cy = (int)(iy + .5); sprintf (buf, " %3dW x %dH\nCenter: %5d @:%6.1f,%6.1f\n Max: %5d @:%4d.0,%4d.0\n Min: %5d Mean: %8.1f\nMedian: %5d StDev: %8.1f", rp->rw, rp->rh, rp->im[cy*rp->iw + cx], ix, iy, ims.max, ims.maxatx, ims.maxaty, ims.min, ims.mean, ims.median, ims.std); XmTextSetString (glstxt_w, buf); } /* plot central row of region rp. * rp is already clamped, rx and rw are the original rp->rx and rp->rw. * N.B. may be called before w is realized, eg, glass row set in app defaults. */ static void drawGlassRow (w, rp, rx, rw) Widget w; ImRegion *rp; int rx, rw; { Display *dsp = XtDisplay (w); Window win = XtWindow(w); XPoint xpcache[256]; XPoint *xps, *xp; Dimension daw, dah; int divs = rw-1; /* fence posts -> rails */ double bot, top; CamPix *im; int i; /* benign if called before being realized */ if (!win) return; /* use stack and avoid malloc if possible */ xps = 2*rw<=XtNumber(xpcache) ? xpcache : (XPoint*)XtMalloc(2*rw*sizeof(XPoint)); xp = xps; /* get size of drawing area */ get_something (w, XmNwidth, (XtArgVal)&daw); get_something (w, XmNheight, (XtArgVal)&dah); /* draw background box and labels first */ bot = h_mini; top = h_maxi; drawGraphGrid (dsp, win, daw, dah, (double)rx, (double)rx+divs,bot,top); /* pixels */ XSetForeground (dsp, imgc, imc1_p); im = &rp->im[(rp->ry+rp->rh/2)*rp->iw + rx]; for (i = 0; i < rw; i++) { if (i + rx >= 0 && rx + i < rp->iw) { CamPix p = im[i]; xp->x = i*(int)daw/divs - (int)daw/divs/2; /* center */ xp->y = (short)((int)dah - (int)dah*(p-bot)/(top-bot)); xp++; xp->x = (i+1)*(int)daw/divs - (int)daw/divs/2; /* exact match */ xp->y = xp[-1].y; xp++; } } XDrawLines (dsp, win, imgc, xps, xp-xps, CoordModeOrigin); if (xps != xpcache) XtFree ((char *)xps); } /* plot central column of region rp. * rp is already clamped, ry and rh are the original rp->ry and rp->rh. * N.B. may be called before w is realized, eg, glass row set in app defaults. */ static void drawGlassCol (w, rp, ry, rh) Widget w; ImRegion *rp; int ry, rh; { Display *dsp = XtDisplay (w); Window win = XtWindow(w); XPoint xpcache[256]; XPoint *xps, *xp; Dimension daw, dah; int divs = rh-1; double bot, top; CamPix *im; int i; /* benign if called before being realized */ if (!win) return; /* use stack and avoid malloc if possible */ xps = 2*rh<=XtNumber(xpcache) ? xpcache : (XPoint*)XtMalloc(2*rh*sizeof(XPoint)); xp = xps; /* get size of drawing area */ get_something (w, XmNwidth, (XtArgVal)&daw); get_something (w, XmNheight, (XtArgVal)&dah); /* draw background box and labels first */ bot = h_mini; top = h_maxi; drawGraphGrid (dsp, win, daw, dah, (double)ry, (double)ry+divs,bot,top); /* pixels */ XSetForeground (dsp, imgc, imc1_p); im = &rp->im[rp->ry*rp->iw + rp->rx + rp->rw/2]; for (i = 0; i < rh; i++) { if (i + ry >= 0 && ry + i < rp->ih) { CamPix p = im[i*rp->iw]; xp->x = i*(int)daw/divs - (int)daw/divs/2; /* center */ xp->y = (short)((int)dah - (int)dah*(p-bot)/(top-bot)); xp++; xp->x = (i+1)*(int)daw/divs - (int)daw/divs/2; /* exact match */ xp->y = xp[-1].y; xp++; } } XDrawLines (dsp, win, imgc, xps, xp-xps, CoordModeOrigin); if (xps != xpcache) XtFree ((char *)xps); } /* print overall image stats */ static void printImageStats(name) char *name; { char buf[1024]; char center[1024]; char *base; short columns; int centerl; /* strip any leading path portion */ if ((base = strrchr (name, '/')) || (base = strrchr (name, '\\'))) name = base+1; /* prepare to center name and size */ centerl = sprintf (center, "%s %dW x %dH", name, fim.sw, fim.sh); get_something (ostxt_w, XmNcolumns, (XtArgVal)&columns); sprintf (buf, "%*s%s\n Max: %5d at:%4d,%4d\n Min: %5d Mean: %8.1f\nMedian: %5d StDev: %8.1f", (columns-centerl)/2, "", center, wims.max, wims.maxatx, wims.maxaty, wims.min, wims.mean, wims.median, wims.std); XmTextSetString (ostxt_w, buf); } /* compute wims based on fim */ static void findWholeImageStats() { ImRegion imr; imr.im = (CamPix *) fim.image; imr.iw = fim.sw; imr.ih = fim.sh; imr.rx = BORDER; imr.ry = BORDER; imr.rw = fim.sw - 2*BORDER; imr.rh = fim.sh - 2*BORDER; regionStats (&imr, &wims); } /* handle the operation of the cross-sectional slice. */ void si_doSlice ( Display *dsp, Window win, int state, /* -1: new 0: erase 1: draw 2: void next erase */ int ww, int wh, /* size of image drawing area */ int wx, int wy, /* cursor coords on drawing area */ int lr, int tb) /* user's flip settings */ { static int startwx, startwy; /* session starting point */ static int lastwx, lastwy; /* last window endpoint x/y */ static GC xogc; /* GC for XORing slice */ if (!fimok) return; if (!xogc) { unsigned long mask; XGCValues gcv; mask = GCFunction | GCForeground; gcv.function = GXxor; gcv.foreground = imc2_p ^ BlackPixel(dsp,DefaultScreen(dsp)); xogc = XCreateGC (dsp, win, mask, &gcv); } switch (state) { case -1: /* new starting pos */ startwx = lastwx = wx; startwy = lastwy = wy; break; case 0: XDrawLine (dsp, win, xogc, startwx, startwy, lastwx, lastwy); break; case 1: XDrawLine (dsp, win, xogc, startwx, startwy, wx, wy); lastwx = wx; lastwy = wy; if (si_ismanaged()) drawSlice (startwx, startwy, ww, wh, wx, wy, lr, tb); break; case 2: lastwx = startwx; lastwy = startwy; break; default: printf ("bad doSlice state: %d\n", state); abort(); break; } } /* draw pixels along line from [x0,y0] to [x1,y1] in window of size wwXwh */ static void drawSlice (x0, y0, ww, wh, x1, y1, lr, tb) int x0, y0, ww, wh, x1, y1, lr, tb; { Display *dsp = XtDisplay (sda_w); Window win = XtWindow (sda_w); CamPix *ip = (CamPix *)fim.image; /* just handy */ Dimension sdaw, sdah; /* sda size */ XPoint xpcache[1024], *xp; int l, h; double len; int sdax; /* length if slice */ len = sqrt(sqr((double)(x1-x0))+sqr((double)(y1-y0)))/fmag; if (len == 0) return; /* get size of drawing area */ get_something (sda_w, XmNwidth, (XtArgVal)&sdaw); get_something (sda_w, XmNheight, (XtArgVal)&sdah); /* use stack if possible to avoid malloc */ xp = sdaw <= XtNumber(xpcache) ? xpcache : (XPoint*)XtMalloc(sdaw*sizeof(XPoint)); /* account for user's flipping */ if (lr) { x0 = ww - 1 - x0; x1 = ww - 1 - x1; } if (tb) { y0 = wh - 1 - y0; y1 = wh - 1 - y1; } /* range */ if (XmToggleButtonGetState (slmax_w)) { h = h_maxi; l = h_mini; } else { h = hipix; l = lopix; } /* walk along */ for (sdax = 0; sdax < sdaw; sdax++) { double ix, iy; int p, wx, wy; wx = x0 + sdax*(x1-x0)/(int)sdaw; wy = y0 + sdax*(y1-y0)/(int)sdaw; si_win2im (wx, wy, ww, wh, &ix, &iy); p = ip[(int)(fim.sw*(int)(iy + .5) + ix + .5)]; xp[sdax].x = sdax; xp[sdax].y = sdah-sdah*(p-l)/(h-l); /* +y is down */ } /* grid */ XSetForeground (dsp, imgc, img_p); drawGraphGrid (dsp, win, sdaw, sdah, 0.0, len, (double)l, (double)h); /* graph */ XSetForeground (dsp, imgc, imc1_p); XDrawLines (dsp, win, imgc, xp, sdaw, CoordModeOrigin); if (xp != xpcache) XtFree ((char *)xp); } /* handle the operation of gaussian measurement. */ void si_doGauss ( Display *dsp, int ww, int wh, /* size of X Window */ int wx, int wy, /* X window coords of cursor */ int lr, int tb) /* user's flip settings */ { int fx, fy; /* flipped X coords */ double ix, iy; /* image coords under cursor */ /* find image coords under cursor */ fx = lr ? ww - 1 - wx : wx; fy = tb ? wh - 1 - wy : wy; si_win2im (fx, fy, ww, wh, &ix, &iy); imPhotom (ix, iy); } /* compute and display photometry of star in glass-size region around image * coords [ix,iy]. * N.B. must be called after doGlass for gaussian overlays to work. */ static void imPhotom (ix, iy) double ix, iy; { Display *dsp = XtD; char buf[256], rastr[32], decstr[32], vfwhmstr[32], hfwhmstr[32]; char magstr[64]; ImRegion imr; /* region to gauss fit */ int len; /* size of region to fit */ int back; /* handy len/2 */ double ra, dec; /* ra/dec of gaussian peak */ double mag, merr; /* magnitude and error */ double vfwhm, hfwhm; /* v and h fwhm, in pixels */ int x, y; /* integral image coords of peak */ /* just paranoid */ if (!fimok) return; /* get star here */ x = (int)(ix + .5); y = (int)(iy + .5); len = glimsz(); back = len/2; imr.im = (CamPix *) fim.image; imr.iw = fim.sw; imr.ih = fim.sh; imr.rx = x - back; imr.ry = y - back; imr.rw = len; imr.rh = len; if (clampRegion (&imr) < 0) return; if (getStar (&imr, &newstar) < 0) { /* blank out if trouble */ if (XmToggleButtonGetState (glrow_w)) XClearWindow (dsp, XtWindow(glrda_w)); if (XmToggleButtonGetState (glcol_w)) XClearWindow (dsp, XtWindow(glcda_w)); XmTextSetString (phtxt_w, ""); return; } newstarok = 1; sv_drawimdot (newstar.x, newstar.y, 5, 1); /* find magnitude compared to reference. * if no current reference, then use this one. */ if (!refstarok) { refstar = newstar; refstarok = 1; } if (cmpStars (&refstar, &newstar, &mag, &merr) < 0) { magstr[0] = '\0'; } else { /* add user's reference value */ char *txt = XmTextFieldGetString (phmagref_w); mag += atof (txt); XtFree (txt); sprintf (magstr, "%6.2f ±%6.2f", mag, merr); } /* print stats of peak */ hfwhm = newstar.hg.s*FWHMSIG; vfwhm = newstar.vg.s*FWHMSIG; if (xy2RADec (&fim, newstar.x, newstar.y, &ra, &dec) == 0) { fs_sexa (rastr, radhr(ra), 2, 360000); fs_sexa (decstr, raddeg(dec), 3, 36000); sprintf (hfwhmstr, "%5.1f\"", fabs(hfwhm*fim.xinc*3600.)); sprintf (vfwhmstr, "%5.1f\"", fabs(vfwhm*fim.yinc*3600.)); } else { hfwhmstr[0] = vfwhmstr[0] = decstr[0] = '\0'; strcpy (rastr, " (No WCS)"); } sprintf (buf, "Mag: %s\n X: %6.1f RA: %s\n Y: %6.1f Dec: %s\nXFWHM:%4.1fp%6s Base:%6.0f\nYFWHM:%4.1fp%6s Peak:%6.0f", magstr, newstar.x, rastr, newstar.y, decstr, hfwhm, hfwhmstr, newstar.hg.B, vfwhm, vfwhmstr, newstar.hg.B+newstar.hg.A); XmTextSetString (phtxt_w, buf); } /* draw stats for current zoom. */ void si_doROI ( Display *dsp, int ww, int wh, /* size of X Window */ int lr, int tb, /* user's flip settings */ ZM_Undo *zp) { int fx, fy; /* flipped screen coords */ double ix, iy; /* image coords */ ImRegion imr; /* ROI region */ int cx, cy; /* cursor image coords */ char buf[1024]; /* info */ /* quiet noop w/o and image */ if (!fimok) return; /* start defining region */ imr.im = (CamPix *) fim.image; imr.iw = fim.sw; imr.ih = fim.sh; /* find image coords at each corner */ fx = lr ? ww - 1 - zp->x0 : zp->x0; fy = tb ? wh - 1 - zp->y0 : zp->y0; si_win2im (fx, fy, ww, wh, &ix, &iy); imr.rx = (int)(ix + .5); imr.ry = (int)(iy + .5); fx = lr ? ww - 1 - zp->x1 : zp->x1; fy = tb ? wh - 1 - zp->y1 : zp->y1; si_win2im (fx, fy, ww, wh, &ix, &iy); cx = (int)(ix + .5); cy = (int)(iy + .5); imr.rw = cx - imr.rx; imr.rh = cy - imr.ry; /* straight if necessary */ if (imr.rw < 0) { imr.rw = -imr.rw; imr.rx -= imr.rw; } if (imr.rh < 0) { imr.rh = -imr.rh; imr.ry -= imr.rh; } /* get stats .. need more than 1 pixel to work with */ if (imr.rw * imr.rh >= 2) { ImStats ims; regionStats (&imr, &ims); sprintf (buf, " Diag:%6.1f WxH:%4d,%4d\nCursor: %5d at:%4d,%4d\n Max: %5d at:%4d,%4d\n Min: %5d Mean: %8.1f\nMedian: %5d StDev: %8.1f", sqrt(sqr((double)imr.rw) + sqr((double)imr.rh)), imr.rw, imr.rh, imr.im[cy*imr.iw + cx], cx, cy, ims.max, ims.maxatx, ims.maxaty, ims.min, ims.mean, ims.median, ims.std); } else buf[0] = '\0'; /* display */ XmTextSetString (rstxt_w, buf); } /* draw a nice labeled graph in the given window */ static void drawGraphGrid (dsp, win, ww, wh, xmin, xmax, ymin, ymax) Display *dsp; Window win; int ww, wh; /* size of win */ double xmin, xmax; /* x range */ double ymin, ymax; /* y range */ { double ticks[NTICKS+2]; /* see tickmarks() */ XSegment xs[2*(NTICKS+2)]; /* room for both */ int nticks, nxs; char buf[32]; int i, l, x, y; /* erase to background */ XSetForeground (dsp, imgc, imbg_p); XFillRectangle (dsp, win, imgc, 0, 0, ww, wh); /* init one set of segments for both directions */ nxs = 0; /* horizontal */ XSetForeground (dsp, imgc, img_p); nticks = tickmarks (ymin, ymax, NTICKS, ticks); for (i = 0; i < nticks; i++) { y = wh - (int)(wh*(ticks[i]-ymin)/(ymax-ymin)); if (y < 0 || y > wh) continue; xs[nxs].x1 = 0; xs[nxs].y1 = y; xs[nxs].x2 = ww; xs[nxs].y2 = y; nxs++; l = sprintf (buf, "%.0f", ticks[i]); XDrawString (dsp, win, imgc, 2, y-1, buf, l); } /* vertical */ nticks = tickmarks (xmin, xmax, NTICKS, ticks); for (i = 0; i < nticks; i++) { x = (int)(ww*(ticks[i]-xmin)/(xmax-xmin)); if (x < 0 || x > ww) continue; xs[nxs].x1 = x; xs[nxs].y1 = 0; xs[nxs].x2 = x; xs[nxs].y2 = wh; nxs++; l = sprintf (buf, "%.0f", ticks[i]); XDrawString (dsp, win, imgc, x+1, wh-1, buf, l); } /* draw grid in one blast */ if (nxs > XtNumber(xs)) { printf ("drawGraphGrid stack corrupted: %d\n", nxs); abort(); } XDrawSegments (dsp, win, imgc, xs, nxs); } /* make some GCs and get some pixels we use in several places */ static void mkGCs() { Display *dsp = XtD; Widget tlw = toplevel_w; Window win = RootWindow(dsp, DefaultScreen(dsp)); Pixel p; imgc = XCreateGC (dsp, win, 0L, NULL); if (get_color_resource (tlw, "GraphBGColor", &imbg_p) < 0) imbg_p = BlackPixel(dsp,DefaultScreen(dsp)); if (get_color_resource (tlw, "ImGridColor", &img_p) < 0) img_p = WhitePixel(dsp,DefaultScreen(dsp)); if (get_color_resource (tlw, "ImPlotColor1", &imc1_p) < 0) imc1_p = WhitePixel(dsp,DefaultScreen(dsp)); if (get_color_resource (tlw, "ImPlotColor2", &imc2_p) < 0) imc2_p = WhitePixel(dsp,DefaultScreen(dsp)); if (get_color_resource (tlw, "ImPlotColor3", &imc3_p) < 0) imc3_p = WhitePixel(dsp,DefaultScreen(dsp)); if (get_color_resource (tlw, "GlassBorderColor", &p) < 0) p = WhitePixel (dsp, 0); glassGC = XCreateGC (dsp, win, 0L, NULL); XSetForeground (dsp, glassGC, p); } /* pick a good initial contrast for the new fim. * use our keywords if present and reasonable. */ static void defContrast() { int lpix, hpix; double gamma; if (!getIntFITS (&fim, lopixkw, &lpix) && lpix >= h_mini && !getIntFITS (&fim, hipixkw, &hpix) && hpix <= h_maxi && !getRealFITS (&fim, gammakw, &gamma) && gamma >= 1./GAM_MAX && gamma <= GAM_MAX) { if (lpix > hpix) { /* implies inverse video */ lopix = hpix; hipix = lpix; want_inv = 1; } else { /* normal video */ lopix = lpix; hipix = hpix; want_inv = 0; } XmToggleButtonSetState (inv_w, want_inv, False); XmScaleSetValue (gamma_w, (int)(gamma*100)); } else { si_wide(); } } /* set up lopix and hipix and a resonable gamma for medium contrast setting */ static void si_mcontrast() { lopix = h_peaki - (h_peaki - h_mini)/2; hipix = h_peaki + (h_maxi - h_peaki)/2; XmScaleSetValue (gamma_w, DEFGAMMA); } /* increase contrast */ static void si_sharper() { lopix += (h_peaki-lopix)/3; hipix -= (hipix-h_peaki)/3; } /* decrease contrast */ static void si_duller() { lopix = (3*lopix - h_peaki)/(3-1); if (lopix < h_mini) lopix = h_mini; hipix = (3*hipix - h_peaki)/(3-1); if (hipix > h_maxi) hipix = h_maxi; if (lopix == h_mini && hipix == h_maxi) { int g; XmScaleGetValue (gamma_w, &g); g -= 10; if (g >= (int)(1./GAM_MAX*100)) XmScaleSetValue (gamma_w, g); } } /* set to narrow statistical contrast */ static void si_narrow() { lopix = (int)(wims.mean - wims.std/3); if (lopix < h_mini) lopix = h_mini; hipix = (int)(wims.mean + wims.std/3); if (hipix > h_maxi) hipix = h_maxi; XmScaleSetValue (gamma_w, DEFGAMMA); } /* set to wide statistical contrast */ static void si_wide() { lopix = (int)(wims.mean - wims.std); if (lopix < h_mini) lopix = h_mini; hipix = (int)(wims.mean + 2*wims.std); if (hipix > h_maxi) hipix = h_maxi; XmScaleSetValue (gamma_w, DEFGAMMA); } /* set to full pixel contrast */ static void si_full() { lopix = h_mini; hipix = h_maxi; XmScaleSetValue (gamma_w, DEFGAMMA); } /* increase brightness */ static void si_brighter() { lopix = (3*lopix - h_peaki)/(3-1); if (lopix < h_mini) lopix = h_mini; hipix -= (hipix-h_peaki)/3; } /* decrease brightness */ static void si_darker() { lopix += (h_peaki-lopix)/3; hipix = (3*hipix - h_peaki)/(3-1); if (hipix > h_maxi) { int g; hipix = h_maxi; XmScaleGetValue (gamma_w, &g); g += 10; if (g < (int)(GAM_MAX*100)) XmScaleSetValue (gamma_w, g); } } /* add a new section control */ static void sectionCtrl (Widget par_w, char *hlptag, Widget *tb, char *tbname, char *tblabel, char *tbtip) { Arg args[20]; Widget w, tb_w, f_w; int n; n = 0; XtSetArg (args[n], XmNspacing, 10); n++; f_w = XmCreateForm (par_w, "SF", args, n); XtManageChild (f_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; tb_w = XmCreateToggleButton (f_w, tbname, args, n); sr_reg (tb_w, NULL, skyipcategory, 0); set_xmstring (tb_w, XmNlabelString, tblabel); wtip (tb_w, tbtip); XtManageChild (tb_w); *tb = tb_w; n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; w = XmCreatePushButton (f_w, "Help", args, n); XtAddCallback (w, XmNactivateCallback, si_help_cb, hlptag); wtip (w, "Get detailed help on this section"); XtManageChild (w); } /* create the imaging dialog */ static void si_createdialog() { Widget mrc_w, rc_w, rc2_w; Widget f_w; Widget ml_w, fr_w; Widget rb_w; Widget w; Arg args[20]; long mask; int i; int n; /* create form */ n = 0; XtSetArg (args[n], XmNautoUnmanage, False); n++; XtSetArg (args[n], XmNallowResize, True); n++; XtSetArg (args[n], XmNverticalSpacing, 10); n++; XtSetArg (args[n], XmNmarginWidth, 10); n++; XtSetArg (args[n], XmNmarginHeight, 10); n++; XtSetArg (args[n], XmNcolormap, xe_cm); n++; XtSetArg (args[n], XmNdefaultPosition, False); n++; si_w = XmCreateFormDialog (svshell_w, "SkyIP", args, n); set_something (si_w, XmNcolormap, (XtArgVal)xe_cm); XtAddCallback (si_w, XmNhelpCallback, si_help_cb, "SkyIP"); sr_reg (XtParent(si_w), "XEphem*SkyIP.x", skyipcategory, 0); sr_reg (XtParent(si_w), "XEphem*SkyIP.y", skyipcategory, 0); /* set some stuff in the parent DialogShell. * setting XmNdialogTitle in the Form didn't work.. */ n = 0; XtSetArg (args[n], XmNtitle, "xephem Sky Image Tools"); n++; XtSetValues (XtParent(si_w), args, n); /* each separately controllable chunk in main RC */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNpacking, XmPACK_TIGHT); n++; XtSetArg (args[n], XmNorientation, XmVERTICAL); n++; XtSetArg (args[n], XmNspacing, 4); n++; XtSetArg (args[n], XmNisAligned, False); n++; XtSetArg (args[n], XmNadjustMargin, False); n++; XtSetArg (args[n], XmNadjustLast, True); n++; mrc_w = XmCreateRowColumn (si_w, "SIMRC", args, n); XtManageChild (mrc_w); /* overall info -- always on */ n = 0; XtSetArg (args[n], XmNmarginWidth, SINDENT); n++; f_w = XmCreateForm (mrc_w, "Image", args, n); XtManageChild (f_w); /* info Text */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNeditable, False); n++; XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; XtSetArg (args[n], XmNcursorPositionVisible, False); n++; XtSetArg (args[n], XmNblinkRate, 0); n++; XtSetArg (args[n], XmNrows, 4); n++; XtSetArg (args[n], XmNcolumns, 29); n++; ostxt_w = XmCreateText (f_w, "GM", args, n); wtip (ostxt_w, "Overall image statistics"); XtManageChild (ostxt_w); /* contrast controls */ sectionCtrl (mrc_w, "SkyIP_Contrast", &ctb_w, "ContrastSection", "Brightness and Contrast", "Whether to show the Contrast section"); n = 0; XtSetArg (args[n], XmNmarginWidth, SINDENT); n++; XtSetArg (args[n], XmNhorizontalSpacing, 5); n++; f_w = XmCreateForm (mrc_w, "CF", args, n); if (XmToggleButtonGetState(ctb_w)) XtManageChild (f_w); XtAddCallback (ctb_w, XmNvalueChangedCallback, si_managetb_cb, (XtPointer)f_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNpacking, XmPACK_COLUMN); n++; XtSetArg (args[n], XmNorientation, XmVERTICAL); n++; XtSetArg (args[n], XmNnumColumns, 3); n++; XtSetArg (args[n], XmNspacing, 3); n++; XtSetArg (args[n], XmNisAligned, False); n++; XtSetArg (args[n], XmNadjustMargin, False); n++; XtSetArg (args[n], XmNadjustLast, False); n++; XtSetArg (args[n], XmNmarginWidth, 0); n++; rc_w = XmCreateRowColumn (f_w, "Contrast", args, n); XtManageChild (rc_w); n = 0; inv_w = XmCreateToggleButton (rc_w, "Reverse", args, n); XtAddCallback (inv_w, XmNvalueChangedCallback, si_inv_cb, 0); wtip (inv_w, "Whether image is displayed black-on-white"); XtManageChild (inv_w); want_inv = XmToggleButtonGetState (inv_w); sr_reg (inv_w, NULL, skyipcategory, 1); n = 0; w = XmCreatePushButton (rc_w, "Nominal", args, n); XtAddCallback (w, XmNactivateCallback, si_contrast_cb, (XtPointer)si_mcontrast); wtip (w, "Set nominal contrast"); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Narrow", args, n); XtAddCallback (w, XmNactivateCallback, si_contrast_cb, (XtPointer)si_narrow); wtip (w, "Set contrast range to -StDev/3 .. +StDev/3"); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Sharper", args, n); XtAddCallback (w, XmNactivateCallback, si_contrast_cb, (XtPointer)si_sharper); wtip (w, "Increase contrast"); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Duller", args, n); XtAddCallback (w, XmNactivateCallback, si_contrast_cb, (XtPointer)si_duller); wtip (w, "Decrease contrast"); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Wide", args, n); XtAddCallback (w, XmNactivateCallback, si_contrast_cb, (XtPointer)si_wide); wtip (w, "Set contrast range to -StDev .. +2*StDev"); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Brighter", args, n); XtAddCallback (w, XmNactivateCallback, si_contrast_cb, (XtPointer)si_brighter); wtip (w, "Increase brightness"); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Darker", args, n); XtAddCallback (w, XmNactivateCallback, si_contrast_cb, (XtPointer)si_darker); wtip (w, "Decrease brightness"); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Full", args, n); XtAddCallback (w, XmNactivateCallback, si_contrast_cb, (XtPointer)si_full); wtip (w, "Set contrast range to show all pixels"); XtManageChild (w); /* min/max fields */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNtopOffset, 4); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; w = XmCreateLabel (f_w, "Low ", args, n); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, w); n++; XtSetArg (args[n], XmNcolumns, 5); n++; lo_w = XmCreateTextField (f_w, "Low", args, n); XtAddCallback (lo_w, XmNactivateCallback, si_lohi_cb, NULL); XtManageChild (lo_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNtopOffset, 4); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, lo_w); n++; w = XmCreateLabel (f_w, " ... ", args, n); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, w); n++; XtSetArg (args[n], XmNcolumns, 5); n++; hi_w = XmCreateTextField (f_w, "Hi", args, n); XtAddCallback (hi_w, XmNactivateCallback, si_lohi_cb, NULL); XtManageChild (hi_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNtopOffset, 4); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, hi_w); n++; w = XmCreateLabel (f_w, "Hi ", args, n); XtManageChild (w); /* gamma scale */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, lo_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNminimum, (int)(1./GAM_MAX*100)); n++; XtSetArg (args[n], XmNmaximum, (int)(GAM_MAX*100)); n++; XtSetArg (args[n], XmNscaleMultiple, 20); n++; XtSetArg (args[n], XmNdecimalPoints, 2); n++; XtSetArg (args[n], XmNshowValue, True); n++; gamma_w = XmCreateScale (f_w, "GammaScale", args, n); XtAddCallback (gamma_w, XmNdragCallback, si_gamma_cb, NULL); XtAddCallback (gamma_w, XmNvalueChangedCallback, si_gamma_cb, NULL); wtip (gamma_w, "Select new Gamma factor: brightness = pixel^Gamma"); XtManageChild (gamma_w); sr_reg (gamma_w, NULL, skyipcategory, 0); /* drawing area in a frame for the contrast map */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, gamma_w); n++; XtSetArg (args[n], XmNtopOffset, 6); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; fr_w = XmCreateFrame (f_w, "HF", args, n); XtManageChild (fr_w); n = 0; fda_w = XmCreateDrawingArea (fr_w, "Histogram", args, n); XtAddCallback (fda_w, XmNexposeCallback, si_exp_cb, NULL); mask = Button1MotionMask | ButtonPressMask | ButtonReleaseMask | PointerMotionHintMask; XtAddEventHandler (fda_w, mask, False, si_motion_eh, NULL); XtManageChild (fda_w); wtip (fda_w, "Histogram and colormap.. slide points to change limits"); /* cross section slice */ sectionCtrl (mrc_w, "SkyIP_Slice", &stb_w, "SliceSection", "Cross section Slice", "Whether to show the Cross-section Slice section"); n = 0; XtSetArg (args[n], XmNmarginWidth, SINDENT); n++; f_w = XmCreateForm (mrc_w, "Slice", args, n); if (XmToggleButtonGetState(stb_w)) { XtManageChild (f_w); svtb_updateSlice (1); } XtAddCallback (stb_w, XmNvalueChangedCallback, si_managetb_cb, (XtPointer)f_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; slmax_w = XmCreateToggleButton (f_w, "FullScale", args, n); set_xmstring (slmax_w, XmNlabelString, "Full pixel range"); wtip (slmax_w, "Whether scale is Full or from Bright/Contrast"); sr_reg (slmax_w, NULL, skyipcategory, 1); XtManageChild (slmax_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, slmax_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; fr_w = XmCreateFrame (f_w, "SF", args, n); XtManageChild (fr_w); n = 0; sda_w = XmCreateDrawingArea (fr_w, "SlicePlot", args, n); XtManageChild (sda_w); wtip (sda_w, "Cross-sectional slice"); /* glass section */ sectionCtrl (mrc_w, "SkyIP_Glass", >b_w, "GlassSection", "Magnifying Glass", "Whether to show the glass statistics section"); n = 0; XtSetArg (args[n], XmNspacing, 2); n++; XtSetArg (args[n], XmNmarginWidth, SINDENT); n++; rc_w = XmCreateRowColumn (mrc_w, "Glass", args, n); if (XmToggleButtonGetState(gtb_w)) { XtManageChild (rc_w); svtb_updateGlass(1); } XtAddCallback (gtb_w, XmNvalueChangedCallback, si_managetb_cb, (XtPointer)rc_w); /* size RB */ n = 0; XtSetArg (args[n], XmNmarginWidth, 0); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; f_w = XmCreateForm (rc_w, "Size", args, n); XtManageChild (f_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; ml_w = XmCreateLabel (f_w, "SL", args, n); set_xmstring (ml_w, XmNlabelString, "Size:"); XtManageChild (ml_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, ml_w); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; rb_w = XmCreateRadioBox (f_w, "GS", args, n); wtip (rb_w, "Glass' final size on screen"); XtManageChild (rb_w); for (i = 0; i < XtNumber(glsz); i++) { RadioSet *rp = &glsz[i]; n = 0; rp->w = XmCreateToggleButton (rb_w, rp->name, args, n); sr_reg (rp->w, NULL, skyipcategory, 1); set_xmstring (rp->w, XmNlabelString, rp->label); XtManageChild (rp->w); } /* mag RB */ n = 0; XtSetArg (args[n], XmNmarginWidth, 0); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; f_w = XmCreateForm (rc_w, "Mag", args, n); XtManageChild (f_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; ml_w = XmCreateLabel (f_w, "ML", args, n); set_xmstring (ml_w, XmNlabelString, " Mag:"); XtManageChild (ml_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, ml_w); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; rb_w = XmCreateRadioBox (f_w, "GM", args, n); wtip (rb_w, "Glass' magnification factor"); XtManageChild (rb_w); for (i = 0; i < XtNumber(glmag); i++) { RadioSet *rp = &glmag[i]; n = 0; rp->w = XmCreateToggleButton (rb_w, rp->name, args, n); sr_reg (rp->w, NULL, skyipcategory, 1); set_xmstring (rp->w, XmNlabelString, rp->label); XtManageChild (rp->w); } /* info Text */ n = 0; XtSetArg (args[n], XmNeditable, False); n++; XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; XtSetArg (args[n], XmNcursorPositionVisible, False); n++; XtSetArg (args[n], XmNblinkRate, 0); n++; XtSetArg (args[n], XmNrows, 5); n++; XtSetArg (args[n], XmNcolumns, 29); n++; glstxt_w = XmCreateText (rc_w, "GM", args, n); wtip (glstxt_w, "Glass' statistics"); XtManageChild (glstxt_w); /* row/col plot controls */ n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; rc2_w = XmCreateRowColumn (rc_w, "Plot", args, n); XtManageChild (rc2_w); n = 0; w = XmCreateLabel (rc2_w, "PCSL", args, n); set_xmstring (w, XmNlabelString, "Plot center: "); XtManageChild (w); n = 0; XtSetArg (args[n], XmNspacing, 3); n++; glrow_w = XmCreateToggleButton (rc2_w, "Row", args, n); sr_reg (glrow_w, NULL, skyipcategory, 0); wtip (glrow_w, "Whether to draw a horizontal cross section"); XtManageChild (glrow_w); n = 0; XtSetArg (args[n], XmNspacing, 3); n++; glcol_w = XmCreateToggleButton (rc2_w, "Column", args, n); sr_reg (glcol_w, NULL, skyipcategory, 0); wtip (glcol_w, "Whether to draw a vertical cross section"); XtManageChild (glcol_w); /* drawing areas for each direction in frames */ n = 0; fr_w = XmCreateFrame (rc_w, "GRF", args, n); XtAddCallback (glrow_w, XmNvalueChangedCallback, si_managetb_cb, (XtPointer)fr_w); if (XmToggleButtonGetState(glrow_w)) XtManageChild (fr_w); /* N.B. since this DA may not be mapped (if the frame is not * managed) take care it is not used before it is really up. */ n = 0; XtSetArg (args[n], XmNresizePolicy, XmRESIZE_NONE); n++; glrda_w = XmCreateDrawingArea (fr_w, "RowPlot", args, n); XtManageChild (glrda_w); wtip (glrda_w, "Plot of central row under glass"); n = 0; fr_w = XmCreateFrame (rc_w, "GCF", args, n); XtAddCallback (glcol_w, XmNvalueChangedCallback, si_managetb_cb, (XtPointer)fr_w); if (XmToggleButtonGetState(glcol_w)) XtManageChild (fr_w); n = 0; XtSetArg (args[n], XmNresizePolicy, XmRESIZE_NONE); n++; glcda_w = XmCreateDrawingArea (fr_w, "ColPlot", args, n); XtManageChild (glcda_w); wtip (glcda_w, "Plot of central column under glass"); /* ROI section */ sectionCtrl (mrc_w, "SkyIP_ROI", &rtb_w, "ROISection", "Region of Interest", "Whether to draw region of interest"); n = 0; XtSetArg (args[n], XmNspacing, 2); n++; XtSetArg (args[n], XmNmarginWidth, SINDENT); n++; rc_w = XmCreateRowColumn (mrc_w, "ROI", args, n); if (XmToggleButtonGetState(rtb_w)) { XtManageChild (rc_w); svtb_updateROI(1); } XtAddCallback (rtb_w, XmNvalueChangedCallback, si_managetb_cb, (XtPointer)rc_w); /* info Text */ n = 0; XtSetArg (args[n], XmNeditable, False); n++; XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; XtSetArg (args[n], XmNcursorPositionVisible, False); n++; XtSetArg (args[n], XmNblinkRate, 0); n++; XtSetArg (args[n], XmNrows, 5); n++; XtSetArg (args[n], XmNcolumns, 29); n++; rstxt_w = XmCreateText (rc_w, "GM", args, n); wtip (rstxt_w, "ROI statistics"); XtManageChild (rstxt_w); /* gaussian section */ sectionCtrl (mrc_w, "SkyIP_2D_Gaussian", &ptb_w, "GaussianSection", "2D Gaussian Measurements", "Whether to show the Gaussian Photometry/Astrometry section"); n = 0; XtSetArg (args[n], XmNmarginWidth, SINDENT); n++; XtSetArg (args[n], XmNverticalSpacing, 4); n++; XtSetArg (args[n], XmNhorizontalSpacing, 4); n++; f_w = XmCreateForm (mrc_w, "Gaussian", args, n); if (XmToggleButtonGetState(ptb_w)) XtManageChild (f_w); XtAddCallback (ptb_w, XmNvalueChangedCallback, si_managetb_cb, (XtPointer)f_w); /* reference */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; ml_w = XmCreateLabel (f_w, "PP", args, n); set_xmstring (ml_w, XmNlabelString, "Reference Mag:"); XtManageChild (ml_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; w = XmCreatePushButton (f_w, "Mark", args, n); XtAddCallback (w, XmNactivateCallback, si_markrefstar_cb, NULL); wtip (w, "Mark the current reference star on the image"); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, w); n++; w = XmCreatePushButton (f_w, "New", args, n); XtAddCallback (w, XmNactivateCallback, si_newrefstar_cb, NULL); wtip (w, "Assign the last star as the new reference star"); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, ml_w); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, w); n++; XtSetArg (args[n], XmNcolumns, 5); n++; phmagref_w = XmCreateTextField (f_w, "RefMag", args, n); XtAddCallback (phmagref_w, XmNactivateCallback, si_newref_cb, NULL); sr_reg (phmagref_w, NULL, skyipcategory, 0); wtip (phmagref_w, "Magnitude to use as reference to other stars"); XtManageChild (phmagref_w); /* info Text */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, phmagref_w); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNeditable, False); n++; XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; XtSetArg (args[n], XmNcursorPositionVisible, False); n++; XtSetArg (args[n], XmNblinkRate, 0); n++; XtSetArg (args[n], XmNrows, 5); n++; XtSetArg (args[n], XmNcolumns, 29); n++; phtxt_w = XmCreateText (f_w, "GM", args, n); XtAddCallback (phtxt_w, XmNactivateCallback, si_newrefstar_cb,NULL); wtip (phtxt_w, "Gaussian solution details"); XtManageChild (phtxt_w); /* bottom PBs */ n = 0; w = XmCreateSeparator (mrc_w, "Sep", args, n); XtManageChild (w); n = 0; f_w = XmCreateForm (mrc_w, "F", args, n); XtManageChild (f_w); n = 0; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 20); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 40); n++; w = XmCreatePushButton (f_w, "Close", args, n); XtAddCallback (w, XmNactivateCallback, si_close_cb, NULL); XtManageChild (w); n = 0; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 60); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 80); n++; w = XmCreatePushButton (f_w, "Help", args, n); XtAddCallback (w, XmNactivateCallback, si_help_cb, "SkyIP"); XtManageChild (w); } /* we just read a fits image into fim -- do everything necessary to display it. * last argument determines whether contrast and WCS are set automatically. */ static void si_newImage(name, autocon) char *name; int autocon; { double ra, dec; watch_cursor(1); /* compute overall image stats */ findWholeImageStats(); /* put the fits fields into the scrolled text */ sf_showHeader(&fim); /* build the histogram */ build_histo(); /* pick a good initial lopix/hipix */ if (autocon) defContrast(); /* assign the colors */ build_colormap(); /* draw the histogram */ si_drawHistogram(); /* establish north and east */ si_ne(); /* show basic stats */ printImageStats(name); /* if find seed values but no WCS run solver, else just display*/ if (autocon && initSolverFields() && xy2RADec(&fim,0,0,&ra,&dec) < 0) wcsMatch (); else sv_newFITS(); watch_cursor(0); } /* set nup and elf from fim */ static void si_ne() { FImage *fip = &fim; double ra1, dec1, ra2, dec2; /* compare upper left corner to center */ if (xy2RADec (fip, fip->sw/2.0, fip->sh/2.0, &ra1, &dec1) < 0) { nup = elf = 1; /* good as any */ return; } (void) xy2RADec (fip, 0.0, 0.0, &ra2, &dec2); nup = (dec2 > dec1); elf = (ra2 > ra1); } /* mark refstar on the image */ /* ARGSUSED */ static void si_markrefstar_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { if (!fimok) xe_msg (1, "First open an image"); else if (!refstarok) xe_msg (1, "Reference star not defined"); else sv_drawimdot (refstar.x, refstar.y, 5, 0); } /* make newstar the new refstar. * N.B. do not use call: used by PB and TF */ /* ARGSUSED */ static void si_newrefstar_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { if (!fimok) xe_msg (1, "First open an image"); else if (newstarok) { refstar = newstar; refstarok = 1; imPhotom (refstar.x, refstar.y); /* show stats */ sv_all(NULL); /* erase */ sv_drawimdot (refstar.x, refstar.y, 5, 0); /* mark */ } else { xe_msg (1, "No star is defined as a photometric reference.\nPlease click on a star to set one."); } } /* user typed ENTER with a new reference value. */ /* ARGSUSED */ static void si_newref_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { if (!fimok) xe_msg (1, "First open an image"); else if (newstarok) { imPhotom (newstar.x, newstar.y); /* reshow stats */ } else { xe_msg (1, "No star is defined as a photometric reference.\nPlease click on a star to set one."); } } /* toggle a manager, in client, on or off. * some also mirror to the toolbar. */ /* ARGSUSED */ static void si_managetb_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { int state = XmToggleButtonGetState(w); Widget sw = (Widget)client; if (state) XtManageChild (sw); else XtUnmanageChild(sw); if (w == stb_w) svtb_updateSlice(state); else if (w == ptb_w) svtb_updateGauss(state); else if (w == rtb_w) svtb_updateROI(state); else if (w == ctb_w) svtb_updateContrast(state); else if (w == gtb_w) svtb_updateGlass(state); } /* ARGSUSED */ static void si_lohi_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { char *lostr = XmTextFieldGetString (lo_w); char *histr = XmTextFieldGetString (hi_w); int lo, hi; lo = atoi(lostr); hi = atoi(histr); if (lo<0 || lo>MAXCAMPIX || hi<0 || hi>MAXCAMPIX || hikw_w); if (!*kw || ((bp = strrchr(kw,' ')) && bp[1] == '\0')) { sprintf (msg, "Please specify %s", wsp->kwtip); ret = -1; goto done; } /* relax need for user to type in upper case */ for (bp = kw; *bp; bp++) if (islower(*bp)) *bp = toupper(*bp); /* get from FITS header and copy to our value field */ if (getStringFITS (&fim, kw, valu) == 0) { XmTextFieldSetString (wsp->v_w, valu); } else if (getRealFITS (&fim, kw, &dvalu) == 0) { sprintf (valu, "%g", dvalu); XmTextFieldSetString (wsp->v_w, valu); } else { sprintf (msg, "Header does not contain a field named '%s'", kw); ret = -1; goto done; } done: XtFree (kw); return (ret); } /* button motion and press/release event handler for the gray map */ /* ARGSUSED */ static void si_motion_eh (w, client, ev, continue_to_dispatch) Widget w; XtPointer client; XEvent *ev; Boolean *continue_to_dispatch; { static int moving_lopix; Display *dsp = XtDisplay(w); Window win = XtWindow(w); Dimension wid, hei; Window root, child; int rx, ry, x, y; unsigned mask; int evt = ev->type; int m1, b1p, b1r; int newpix; /* do nothing if no current image */ if (!fimok) return; /* what happened? */ b1p = evt == ButtonPress && ev->xbutton.button == Button1; b1r = evt == ButtonRelease && ev->xbutton.button == Button1; m1 = evt == MotionNotify && ev->xmotion.state & Button1Mask; /* ignore everything else */ if (!b1p && !m1) return; watch_cursor(1); /* where are we? */ XQueryPointer (dsp, win, &root, &child, &rx, &ry, &x, &y, &mask); get_something (w, XmNwidth, (XtArgVal)&wid); get_something (w, XmNheight, (XtArgVal)&hei); if (x < 0) x = 0; if (x >= (int)wid) x = wid-1; if (y < 0) y = 0; if (y >= (int)hei) y = hei-1; /* scale x to pixel value */ newpix = h_mini + x*(h_maxi - h_mini)/(int)wid; /* if button was just pressed, choose which end to track */ if (b1p) moving_lopix = newpix < (lopix + hipix)/2; /* track the current end -- but never cross over */ if (moving_lopix && newpix < hipix) lopix = newpix; else if (!moving_lopix && newpix > lopix) hipix = newpix; /* rebuild colormap and redraw histogram to show new markers */ build_colormap(); si_drawHistogram(); /* display net result */ sv_dspFITS(); watch_cursor(0); } /* expose callback for the gray map */ /* ARGSUSED */ static void si_exp_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { XmDrawingAreaCallbackStruct *c = (XmDrawingAreaCallbackStruct *)call; Display *dsp = XtDisplay(fda_w); Window win = XtWindow(fda_w); switch (c->reason) { case XmCR_EXPOSE: { static int before; XExposeEvent *e = &c->event->xexpose; if (!before) { XSetWindowAttributes swa; unsigned long mask = CWBitGravity; swa.bit_gravity = ForgetGravity; XChangeWindowAttributes (dsp, win, mask, &swa); before = 1; } /* wait for the last in the series */ if (e->count != 0) return; break; } default: printf ("Unexpected fda_w event. type=%d\n", c->reason); abort(); } si_drawHistogram(); } /* callback to set a preset contrast level. * client is pointer to function that sets lo/hipix (and maybe gamma) */ /* ARGSUSED */ static void si_contrast_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { void (*pfv)() = (void (*)())client; if (!fimok) return; /* implement desired contrast */ (*pfv)(); /* reassign the colors */ build_colormap(); /* redraw histogram to show new markers */ si_drawHistogram(); /* tell skyview to redraw */ sv_dspFITS(); } /* callback from Drag or ValueChanged on the gamma scale */ /* ARGSUSED */ static void si_gamma_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { if (!fimok) return; watch_cursor(1); build_colormap(); si_drawHistogram(); sv_dspFITS(); watch_cursor(0); } /* callback for the gray map Inverse Vid TB */ /* ARGSUSED */ static void si_inv_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { want_inv = XmToggleButtonGetState(w); if (!fimok) return; sv_dspFITS(); } /* draw histgram, if any, in the drawing area, and the lo/hi markers. * we stretch and only show from h_mini .. h_maxi * N.B. we might be called before the first expose */ static void si_drawHistogram() { #define MAXPTS 50 Display *dsp = XtDisplay(fda_w); Window win = XtWindow(fda_w); int h_peak; XPoint pts[MAXPTS]; Dimension wid, hei; char buf[32]; int np; int x; /* might not be far enough */ if (!win || !histo) return; h_peak = histo[h_peaki]; /* nothing to do if no image yet */ if (!fimok) return; /* get window size */ get_something (fda_w, XmNwidth, (XtArgVal)&wid); get_something (fda_w, XmNheight, (XtArgVal)&hei); /* clear */ XSetForeground (dsp, imgc, imbg_p); XFillRectangle (dsp, win, imgc, 0, 0, wid, hei); /* draw the histogram all across each x of the window */ XSetForeground (dsp, imgc, imc1_p); for (np = x = 0; x < (int)wid; x++) { int i, p, y; int l, h; /* find lo and hi pixel values binned into this x */ l = h_mini + x*(h_maxi - h_mini)/(int)wid; h = h_mini + (x+1)*(h_maxi - h_mini)/(int)wid; /* find peak in histogram portion shown at this x */ p = 0; h = h < MAXCAMPIX ? h : MAXCAMPIX; for (i = l; i < h; i++) if (histo[i] > p) p = histo[i]; /* scale to window y value */ y = (int)hei-1 - p*((int)hei-1)/h_peak; /* add another line segment if its y changes */ if (np == 0 || y != pts[np-1].y || x == (int)wid-1) { XPoint *xp = &pts[np++]; xp->x = x; xp->y = y; } if (np == MAXPTS) { XDrawLines (dsp, win, imgc, pts, np, CoordModeOrigin); pts[0].x = pts[np-1].x; pts[0].y = pts[np-1].y; np = 1; } } if (np > 1) XDrawLines (dsp, win, imgc, pts, np, CoordModeOrigin); /* then overlay with the colormap within the lo/hi */ XSetForeground (dsp, imgc, imc2_p); for (np = x = 0; x < (int)wid; x++) { int i, p, y; int l, h; /* find lo and hi pixel values binned into this x */ l = h_mini + x*(h_maxi - h_mini)/(int)wid; h = h_mini + (x+1)*(h_maxi - h_mini)/(int)wid; /* only draw inside the indicators */ if (h < lopix || l > hipix) continue; /* find peak in colormap portion shown at this x */ p = 0; h = h < MAXCAMPIX ? h : MAXCAMPIX; for (i = l; i < h; i++) if (colormap[i] > p) p = colormap[i]; /* scale to window y value */ y = (int)hei-1 - p*((int)hei-1)/(ngray-1); /* add another line segment if its y changes */ if (np == 0 || y != pts[np-1].y || x == (int)wid-1) { XPoint *xp = &pts[np++]; xp->x = x; xp->y = y; } if (np == MAXPTS) { XDrawLines (dsp, win, imgc, pts, np, CoordModeOrigin); pts[0].x = pts[np-1].x; pts[0].y = pts[np-1].y; np = 1; } } if (np > 1) XDrawLines (dsp, win, imgc, pts, np, CoordModeOrigin); /* add the indicators */ XSetForeground (dsp, imgc, imc3_p); x = (lopix - h_mini)*(int)wid/(h_maxi - h_mini); pts[0].x = x-MMW; pts[0].y = 0; pts[1].x = x; pts[1].y = MMH; pts[2].x = x+MMW; pts[2].y = 0; XFillPolygon (dsp, win, imgc, pts, 3, Convex, CoordModeOrigin); x = (hipix - h_mini)*(int)wid/(h_maxi - h_mini); pts[0].x = x-MMW; pts[0].y = 0; pts[1].x = x; pts[1].y = MMH; pts[2].x = x+MMW; pts[2].y = 0; XFillPolygon (dsp, win, imgc, pts, 3, Convex, CoordModeOrigin); /* add the numeric values too */ sprintf (buf, "%5d", lopix); XmTextFieldSetString (lo_w, buf); sprintf (buf, "%5d", hipix); XmTextFieldSetString (hi_w, buf); } /* set up glass' resources according to current size and mag selections. */ static void glassSetup () { glassSize(); makeGlassImage (); } /* set glassmag/sz from widgets */ static void glassSize() { int i; /* get current mag and and size */ glassmag = 0; for (i = 0; i < XtNumber(glmag); i++) { if (XmToggleButtonGetState(glmag[i].w)) { glassmag = glmag[i].value; break; } } if (!glassmag) { printf ("Glass mag radiobox broke!\n"); abort(); } glasssz = 0; for (i = 0; i < XtNumber(glsz); i++) { if (XmToggleButtonGetState(glsz[i].w)) { glasssz = glsz[i].value; break; } } if (!glasssz) { printf ("Glass size radiobox broke!\n"); abort(); } } /* (re)make glass_xim of size glasssz and same genre as fim. */ static void makeGlassImage () { Display *dsp = XtD; char *glassdata; int nbytes; /* free any old then create with new size */ if (glass_xim) { free((char *)glass_xim->data); glass_xim->data = NULL; XDestroyImage(glass_xim); glass_xim = 0; } nbytes = (glasssz+7) * (glasssz+7) * fbpp/8; glassdata = XtMalloc (nbytes); if (!glassdata) { xe_msg (0, "Can not malloc %d for Glass pixels", nbytes); return; } glass_xim = XCreateImage (dsp, XDefaultVisual (dsp, DefaultScreen(dsp)), /* depth */ fbpp == 1 ? 1 : fdepth, /* format */ fbpp == 1 ? XYBitmap : ZPixmap, /* offset */ 0, /* data */ glassdata, /* width */ glasssz, /* height */ glasssz, /* pad */ fbpp < 8 ? 8 : fbpp, /* bpl */ 0); if (!glass_xim) { free ((void *)glassdata); xe_msg (0, "Can not make Glass XImage"); return; } glass_xim->bitmap_bit_order = LSBFirst; glass_xim->byte_order = LSBFirst; } /* fill glass_xim with glasssz (X pixels) view of fim centered at X * coords xc,yc. take care at the edges. */ /* ARGSUSED */ static void fillGlass (xc, yc) int xc, yc; /* center of glass, in X window coords */ { int isz = glasssz/glassmag; /* size of patch image */ int x, y, w, h; /* pixmap patch to extract */ int gx, gy; /* glass coords */ int px, py; /* patch coords */ Pixel p; /* get mag patch from pixmap and save in LR corner of glass image */ px = py = glasssz-isz; x = xc-isz/2; if (x < 0) { px -= x; x = 0; } y = yc-isz/2; if (y < 0) { py -= y; y = 0; } w = h = isz; if (x + w > fpmw) w = fpmw - x; if (y + h > fpmh) h = fpmh - y; XGetSubImage (XtD, fpm, x, y, w, h, AllPlanes, fbpp == 1 ? XYBitmap : ZPixmap, glass_xim, px, py); /* spread patch out into entire glass in place */ px = py = glasssz-isz; for (gy = 0; gy < glasssz; gy += glassmag) { for (gx = 0; gx < glasssz; gx += glassmag) { p = XGetPixel (glass_xim, px+gx/glassmag, py+gy/glassmag); for (y = 0; y < glassmag; y++) { for (x = 0; x < glassmag; x++) { XPutPixel (glass_xim, gx+x, gy+y, p); } } } } } /* compare two ObjF wrt f_mag, qsort style */ static int objf_qsortf (const void *f1p, const void *f2p) { Obj *o1p = (Obj *)f1p; Obj *o2p = (Obj *)f2p; double magdiff = get_mag(o1p) - get_mag(o2p); return (magdiff == 0 ? 0 : (magdiff < 0 ? -1 : 1)); } /* find WCS solution for current image. */ static void wcsMatch () { Now *np = mm_get_now(); double mag; double ra0, dec0, right0, up0, rot0; double bestsep, worstsep; double *sx, *sy; int oldnodups; FImage tmpfi; int burnt; double std; double fov; char msg[1024]; char *txt; ObjF *fsp; int nis, nfs; /* n image, n field stars */ /* get starting conditions */ if (!fimok) { xe_msg (1, "First open an image"); return; } if (getWCSSeed (&wcsseed[WCS_RA], &ra0) < 0) return; if (getWCSSeed (&wcsseed[WCS_DEC], &dec0) < 0) return; if (getWCSSeed (&wcsseed[WCS_USCALE], &up0) < 0) return; if (getWCSSeed (&wcsseed[WCS_RSCALE], &right0) < 0) return; if (getWCSSeed (&wcsseed[WCS_ROT], &rot0) < 0) return; txt = XmTextFieldGetString (burnt_w); burnt = atoi (txt); XtFree (txt); txt = XmTextFieldGetString (snr_w); std = atof (txt); XtFree (txt); txt = XmTextFieldGetString (bsep_w); bestsep = atof (txt); XtFree (txt); txt = XmTextFieldGetString (wsep_w); worstsep = atof (txt); XtFree (txt); sv_dspFITS(); /* clean */ /* get image stars, sorted by brightness */ nis = quickStars (&fim, &wims, BORDER, burnt, std, &sx, &sy); if (nis < 0) { xe_msg (1, "No memory to find stars"); return; } xe_msg (0,"Using %d image stars @ burnt = %d, SNR = %g", nis,burnt,std); /* get field stars in vicinity, sort by brightness. * N.B. turn off nodups so we are imune to loaded catalogs */ fov = FOVMORE*sqrt (sqr(right0*fim.sw) + sqr(up0*fim.sh)); mag = fov > degrad(10) ? 15 : 20; /* no USNO if really big */ oldnodups = fs_setnodups (0); nfs = fs_fetch (np, ra0, dec0, fov, mag, &fsp); fs_setnodups (oldnodups); if (nfs <= 0) { free ((char *)sx); free ((char *)sy); xe_msg (1, "Field stars are required for pattern matching"); return; } qsort ((void*)fsp, nfs, sizeof(*fsp), objf_qsortf); xe_msg (0, "Using %d field stars, magnitude range %g .. %g", nfs, get_mag ((Obj*)&fsp[0]), get_mag((Obj*)&fsp[nfs-1])); /* initial rough coord system. * N.B. take care with tmpfi since this copy shares the same pointers */ tmpfi = fim; tmpfi.xref = raddeg(ra0); tmpfi.yref = raddeg(dec0); tmpfi.xrefpix = fim.sw/2; tmpfi.yrefpix = fim.sh/2; tmpfi.xinc = raddeg(right0); tmpfi.yinc = raddeg(up0); tmpfi.rot = raddeg(rot0); strcpy (tmpfi.type, "-TAN"); tmpfi.wcsset = 1; /* here we go */ nfs = fsmatch (&tmpfi, sv_drawimdot, fsp, nfs, sx, sy, nis, bestsep, worstsep, msg); if (nfs < 0) { free ((char *)fsp); free ((char *)sx); free ((char *)sy); xe_msg (1, "%s", msg); return; } /* ok! install new result */ fim = tmpfi; /* show with its proud new WCS solution */ sf_showHeader (&fim); (void) initSolverFields(); si_ne(); sv_newFITS(); /* mark and report stats of solution */ matchStats (fsp, sx, sy, nfs); /* clean up */ free ((char *)fsp); free ((char *)sx); free ((char *)sy); } /* report just how well fim supposedly maps these stars to these image coords. */ static void matchStats (fsp, sx, sy, nfs) ObjF *fsp; double *sx, *sy; int nfs; { double m, *med; double sum; Obj *bestop, *worstop; double best, worst; double asppix; int i; /* sanity check */ if (nfs < 1) return; /* get an array for finding median err */ med = (double*)XtMalloc(nfs*sizeof(double)); /* scan to build stats */ sum = 0; worst = 0; best = 1e9; bestop = worstop = NULL; for (i = 0; i < nfs; i++) { Obj *op = (Obj *)fsp++; double x, y, err; RADec2xy (&fim, op->f_RA, op->f_dec, &x, &y); err = sqrt(sqr(x-sx[i]) + sqr(y-sy[i])); if (err > worst) { worst = err; worstop = op; } if (err < best) { best = err; bestop = op; } sum += err; med[i] = err; sv_drawimdot (sx[i], sy[i], 5, 0); sv_drawimdot (x, y, 7, 1); } m = dmedian (med, nfs); /* report info */ asppix = 3600*sqrt(sqr((double)fim.xinc) + sqr((double)fim.yinc)); xe_msg (confirm(), "Found WCS Solution using %d star pairs\n%5.2fp = %5.2f\" minimum error at %s\n%5.2fp = %5.2f\" maximum error at %s\n%5.2fp = %5.2f\" mean error\n%5.2fp = %5.2f\" median error", nfs, best, best*asppix, bestop->o_name, worst, worst*asppix, worstop->o_name, sum/nfs, sum/nfs*asppix, m, m*asppix); /* cleanup */ XtFree ((char *)med); } /* return size of glass in whole image pixels, rounded up */ static int glimsz() { int proper = (int)ceil(glasssz/glassmag/fmag); return (proper >= MINGLSZ ? proper : MINGLSZ); } /* WCS dialog stuff */ /* called to bring up the wcs solver dialog */ void siwcs_manage() { si_create(); XtManageChild(wcs_w); } /* called to bring down the wcs solver dialog */ void siwcs_unmanage() { if (wcs_w) XtUnmanageChild (wcs_w); } /* set up the WCS solver fields from fip keywords, if possible. * return whether all fields are found. */ static int initSolverFields () { char msg[1024]; int ok = 1; int i; si_create(); for (i = 0; i < XtNumber(wcsseed); i++) if (setWCSField (&wcsseed[i], msg) < 0) ok = 0; return (ok); } /* create the WCS dialog */ static void si_createwcsdialog() { Widget sep_w, rc_w; Widget w; Arg args[20]; int i; int n; /* create form */ n = 0; XtSetArg (args[n], XmNautoUnmanage, False); n++; XtSetArg (args[n], XmNallowResize, True); n++; XtSetArg (args[n], XmNverticalSpacing, 10); n++; XtSetArg (args[n], XmNmarginWidth, 10); n++; XtSetArg (args[n], XmNmarginHeight, 10); n++; XtSetArg (args[n], XmNcolormap, xe_cm); n++; XtSetArg (args[n], XmNdefaultPosition, False); n++; wcs_w = XmCreateFormDialog (svshell_w, "WCS", args, n); set_something (wcs_w, XmNcolormap, (XtArgVal)xe_cm); XtAddCallback (wcs_w, XmNhelpCallback, si_wcshelp_cb, NULL); sr_reg (XtParent(wcs_w), "XEphem*WCS.x", skyipcategory, 0); sr_reg (XtParent(wcs_w), "XEphem*WCS.y", skyipcategory, 0); /* set some stuff in the parent DialogShell. * setting XmNdialogTitle in the Form didn't work.. */ n = 0; XtSetArg (args[n], XmNtitle, "xephem WCS solver"); n++; XtSetValues (XtParent(wcs_w), args, n); /* most go in an rc */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNpacking, XmPACK_COLUMN); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNnumColumns, XtNumber(wcsseed)+2); n++; XtSetArg (args[n], XmNspacing, 4); n++; XtSetArg (args[n], XmNisAligned, False); n++; XtSetArg (args[n], XmNadjustMargin, True); n++; XtSetArg (args[n], XmNadjustLast, True); n++; XtSetArg (args[n], XmNmarginWidth, SINDENT); n++; rc_w = XmCreateRowColumn (wcs_w, "WCS", args, n); XtManageChild (rc_w); for (i = 0; i < XtNumber(wcsseed); i++) { WCSSeed *wsp = &wcsseed[i]; char kwres[64]; addLabel (rc_w, wsp->label, -1); wsp->v_w = addTF (rc_w, wsp->res, 1); sr_reg (wsp->v_w, NULL, skyipcategory, 0); wtip (wsp->v_w, wsp->vtip); w = addPB (rc_w, "Use field:", 0); wtip (w,"Click to search FITS header for field named at right"); XtAddCallback (w, XmNactivateCallback, si_wcsuse_cb, (XtPointer)wsp); sprintf (kwres, "%sField", wsp->res); wsp->kw_w = addTF (rc_w, kwres, 1); XtAddCallback (wsp->kw_w, XmNactivateCallback, si_wcsuse_cb, (XtPointer)wsp); sr_reg (wsp->kw_w, NULL, skyipcategory, 1); wtip (wsp->kw_w, wsp->kwtip); } addLabel (rc_w, "Burned out:", -1); burnt_w = addTF (rc_w, "BurnedOut", 1); XtAddCallback (burnt_w, XmNactivateCallback, si_markstars_cb, 0); sr_reg (burnt_w, NULL, skyipcategory, 1); wtip (burnt_w, "Maximum pixel value to trust"); XtManageChild (burnt_w); addLabel (rc_w, "S/N ratio:", -1); snr_w = addTF (rc_w, "SNR", 1); XtAddCallback (snr_w, XmNactivateCallback, si_markstars_cb, 0); sr_reg (snr_w, NULL, skyipcategory, 1); wtip (snr_w, "Minimum standard deviation above mean for stars"); XtManageChild (snr_w); addLabel (rc_w, "Max pix acc:", -1); bsep_w = addTF (rc_w, "BestAcc", 1); sr_reg (bsep_w, NULL, skyipcategory, 1); wtip (bsep_w, "Best accuracy this image can possibly produce, pixels"); XtManageChild (bsep_w); addLabel (rc_w, "Min pix acc:", -1); wsep_w = addTF (rc_w, "WorstAcc", 1); sr_reg (wsep_w, NULL, skyipcategory, 1); wtip (wsep_w, "Worst accuracy we will accept, pixels"); XtManageChild (wsep_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; sep_w = XmCreateSeparator (wcs_w, "Sep", args, n); XtManageChild (sep_w); /* bottom PBs */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, sep_w); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 4); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 24); n++; w = XmCreatePushButton (wcs_w, "Close", args, n); XtAddCallback (w, XmNactivateCallback, si_wcsclose_cb, NULL); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, sep_w); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 28); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 48); n++; w = XmCreatePushButton (wcs_w, "Go", args, n); XtAddCallback (w, XmNactivateCallback, si_wcsgo_cb, 0); wtip (w, "Begin solving for WCS headers"); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, sep_w); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 52); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 72); n++; w = XmCreatePushButton (wcs_w, "MS", args, n); set_xmstring (w, XmNlabelString, "Mark stars"); wtip (w, "Mark the image stars that will be used in the solution"); XtAddCallback (w, XmNactivateCallback, si_markstars_cb, 0); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, sep_w); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 76); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 96); n++; w = XmCreatePushButton (wcs_w, "Help", args, n); XtAddCallback (w, XmNactivateCallback, si_wcshelp_cb, NULL); XtManageChild (w); } /* add a Label to a RC */ static void addLabel (rc_w, label, alignment) Widget rc_w; char *label; int alignment; { Arg args[20]; Widget w; int n; n = 0; if (alignment < 0) { XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; } else if (alignment > 0) { XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++; } else { XtSetArg (args[n], XmNalignment, XmALIGNMENT_CENTER); n++; } w = XmCreateLabel (rc_w, "L", args, n); XtManageChild (w); set_xmstring (w, XmNlabelString, label); } /* add a PB to a RC */ static Widget addPB (rc_w, str, alignment) Widget rc_w; char *str; int alignment; { Arg args[20]; Widget w; int n; n = 0; if (alignment < 0) { XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; } else if (alignment > 0) { XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++; } else { XtSetArg (args[n], XmNalignment, XmALIGNMENT_CENTER); n++; } w = XmCreatePushButton (rc_w, "L", args, n); XtManageChild (w); set_xmstring (w, XmNlabelString, str); return (w); } /* add a TextField to a RC */ static Widget addTF (rc_w, name, rw) Widget rc_w; char *name; int rw; { Arg args[20]; Widget w; int n; n = 0; XtSetArg (args[n], XmNcolumns, 14); n++; XtSetArg (args[n], XmNeditable, rw); n++; XtSetArg (args[n], XmNcursorPositionVisible, rw); n++; w = XmCreateTextField (rc_w, name, args, n); XtManageChild (w); return (w); } /* ARGSUSED */ static void si_wcsclose_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { XtUnmanageChild (wcs_w); } /* called to start a WCS solution */ /* ARGSUSED */ static void si_wcsgo_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { watch_cursor (1); wcsMatch (); watch_cursor (0); } /* called to mark seed stars */ /* ARGSUSED */ static void si_markstars_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { double *sx, *sy; int burnt; double std; char *txt; int nis; int i; if (!fimok) { xe_msg (1, "First open an image"); return; } watch_cursor (1); /* get max pix to use */ txt = XmTextFieldGetString (burnt_w); burnt = atoi (txt); XtFree (txt); if (burnt < 0 || burnt > 65535) { watch_cursor (0); xe_msg (1, "Value for burned out pixel is missing or bogus.\nMust be in range 0 .. 65535"); return; } /* get SNR */ txt = XmTextFieldGetString (snr_w); std = atof (txt); XtFree (txt); /* extract list of brighter isolated pixels as seeds for stars */ nis = quickStars (&fim, &wims, BORDER, burnt, std, &sx, &sy); if (nis < 0) { xe_msg (1, "No memory to find stars"); return; } xe_msg (0,"Found %d image stars @ burnt = %d, SNR = %g", nis,burnt,std); /* draw */ sv_all(NULL); /* clean */ for (i = 0; i < nis; i++) sv_drawimdot (sx[i], sy[i], 5, 0); /* clean up */ free ((char *)sx); free ((char *)sy); watch_cursor (0); } /* called to plug in a nominal WCS value from an existing field. * N.B. don't use call: this is used for a PB and a TF * client is pointer into wcsseed[]. */ /* ARGSUSED */ static void si_wcsuse_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { WCSSeed *wsp = (WCSSeed *)client; char msg[1024]; /* do nothing if no current image */ if (!fimok) { xe_msg (1, "First open an image"); return; } if (setWCSField (wsp, msg) < 0) xe_msg (1, msg); } /* ARGSUSED */ static void si_wcshelp_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { static char *msg[] = { "Pattern match with field stars to find a WCS solution." }; hlp_dialog ("SkyIP_WCS", msg, sizeof(msg)/sizeof(msg[0])); }