XEphem/GUI/xephem/closemenu.c

1227 lines
32 KiB
C

/* code to handle the close objects menu.
*
* the basic idea is to sort all objects into bands of dec (or alt).
* then scan for pairs of close objects within one and adjacent bands.
*/
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/CascadeB.h>
#include <Xm/SelectioB.h>
#include <Xm/ToggleB.h>
#include <Xm/RowColumn.h>
#include <Xm/List.h>
#include <Xm/Separator.h>
#include <Xm/TextF.h>
#include "xephem.h"
#define MINSEP degrad(.1) /* smallest sep we allow.
* this is not a hard limit, just a sanity
* check. as sep shrinks, we make more and
* more tiny little malloced bands.
*/
/* record of a band.
* opp is malloced -- can hold nmem, nuse of which are in use.
*/
typedef struct {
int nuse; /* number of Obj * actually in use in opp now */
int nmem; /* number of Obj * there is room for in opp now */
Obj **opp; /* malloced list of Object pointers, or NULL */
} Band;
/* record of a pair.
* used to collect pairs as the bands are scanned.
*/
typedef struct {
Obj *op1, *op2; /* two objects */
float sep; /* separation, rads (float to save memory) */
} Pair;
static void c_create_shell (void);
static void c_help_cb (Widget w, XtPointer client, XtPointer call);
static void c_helpon_cb (Widget w, XtPointer client, XtPointer call);
static void c_popdown_cb (Widget w, XtPointer client, XtPointer call);
static void c_close_cb (Widget w, XtPointer client, XtPointer call);
static void c_flist_cb (Widget w, XtPointer client, XtPointer call);
static void c_actlist_cb (Widget w, XtPointer client, XtPointer call);
static void c_skypoint_cb (Widget w, XtPointer client, XtPointer call);
static void c_go_cb (Widget w, XtPointer client, XtPointer call);
static int sky_point (void);
static void do_search (void);
static int get_options (double *sp, double *mp, int *wp, int *op);
static void free_band (Band bp[], int nb);
static int init_bands (double sep, double mag, Band **bpp);
static int add_obj (Band *bp, Obj *op);
static int find_close (Band bp[], int nb, double maxsep, int wander, int
ownmoons, Pair **ppp);
static int add_pair (Obj *op1, Obj *op2, double sep, Pair **ppp,
int *mp, int *np);
static int add_pair (Obj *op1, Obj *op2, double sep, Pair **ppp,
int *mp, int *np);
static int dspl_pairs (Pair p[], int np, double sep);
static void c_create_flist_w (void);
static void c_flistok_cb (Widget w, XtPointer client, XtPointer call);
static void flistok_append_cb (void);
static void flistok_overwrite_cb (void);
static void make_flist (char *name, char *how);
static void write_flist (FILE *fp);
static Widget cshell_w; /* main shell */
static Widget sep_w; /* text widget to hold max sep */
static Widget mag_w; /* text widget to hold mag limit */
static Widget timestmp_w; /* label for time stamp */
static Widget count_w; /* label for count of objects in list */
static Widget list_w; /* the list of close objects */
static Widget flist_w; /* the file list dialog */
static Widget wander_w; /* toggle to omit pairs of fixed objs*/
static Widget ownmoons_w; /* toggle to omit a planet's own moons */
static Widget autoup_w; /* toggle to automatically redo on updates */
static Widget autols_w; /* toggle to automatically list on updates */
static char closecategory[] = "Close pairs"; /* Save category */
/* bring up Close pairs shell, creating if first time */
void
c_manage()
{
if (!cshell_w) {
c_create_shell();
c_create_flist_w ();
}
XtPopup (cshell_w, XtGrabNone);
set_something (cshell_w, XmNiconic, (XtArgVal)False);
}
/* called when time has advanced.
* compute a new Close list if desired.
*/
/* ARGSUSED */
void
c_update (np, force)
Now *np;
int force;
{
if (!isUp(cshell_w) || !XmToggleButtonGetState (autoup_w))
return;
do_search();
}
/* called to put up or remove the watch cursor. */
void
c_cursor (c)
Cursor c;
{
Window win;
if (cshell_w && (win = XtWindow(cshell_w)) != 0) {
Display *dsp = XtDisplay(cshell_w);
if (c)
XDefineCursor (dsp, win, c);
else
XUndefineCursor (dsp, win);
}
}
/* create a form in a shell to allow user to work with the list. */
static void
c_create_shell ()
{
typedef struct {
char *label; /* what goes on the help label */
char *key; /* string to call hlp_dialog() */
} HelpOn;
static HelpOn helpon[] = {
{"Intro...", "CloseObjects"},
{"on Control...", "CloseObjects_control"},
{"on Options...", "CloseObjects_options"},
{"Misc...", "CloseObjects_misc"},
};
Widget mb_w, pd_w, cb_w;
Widget s_w;
XmString str;
Widget cform_w;
Widget w;
Arg args[20];
int n;
int i;
/* create outter shell and form */
n = 0;
XtSetArg (args[n], XmNcolormap, xe_cm); n++;
XtSetArg (args[n], XmNtitle, "xephem Close Pairs"); n++;
XtSetArg (args[n], XmNiconName, "Close"); n++;
XtSetArg (args[n], XmNdeleteResponse, XmUNMAP); n++;
cshell_w = XtCreatePopupShell ("CloseObjs", topLevelShellWidgetClass,
toplevel_w, args, n);
setup_icon (cshell_w);
set_something (cshell_w, XmNcolormap, (XtArgVal)xe_cm);
XtAddCallback (cshell_w, XmNpopdownCallback, c_popdown_cb, NULL);
sr_reg (cshell_w, "XEphem*CloseObjs.width", closecategory, 0);
sr_reg (cshell_w, "XEphem*CloseObjs.height", closecategory, 0);
sr_reg (cshell_w, "XEphem*CloseObjs.x", closecategory, 0);
sr_reg (cshell_w, "XEphem*CloseObjs.y", closecategory, 0);
n = 0;
XtSetArg (args[n], XmNresizePolicy, XmRESIZE_NONE); n++;
XtSetArg (args[n], XmNverticalSpacing, 5); n++;
cform_w = XmCreateForm(cshell_w, "CloseSh", args, n);
XtAddCallback (cform_w, XmNhelpCallback, c_help_cb, NULL);
XtManageChild (cform_w);
/* create the menu bar across the top */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
mb_w = XmCreateMenuBar (cform_w, "MB", args, n);
XtManageChild (mb_w);
/* make the Control pulldown */
n = 0;
pd_w = XmCreatePulldownMenu (mb_w, "ControlPD", args, n);
n = 0;
XtSetArg (args[n], XmNsubMenuId, pd_w); n++;
XtSetArg (args[n], XmNmnemonic, 'C'); n++;
cb_w = XmCreateCascadeButton (mb_w, "ControlCB", args, n);
set_xmstring (cb_w, XmNlabelString, "Control");
XtManageChild (cb_w);
/* make the Go button */
n = 0;
w = XmCreatePushButton (pd_w, "Run", args, n);
XtAddCallback (w, XmNactivateCallback, c_go_cb, 0);
wtip (w, "Perform one scan for close pairs");
XtManageChild (w);
/* make the sky point button */
n = 0;
w = XmCreatePushButton (pd_w, "SkyPt", args, n);
XtAddCallback (w, XmNactivateCallback, c_skypoint_cb, 0);
set_xmstring(w, XmNlabelString, "Sky point");
wtip (w, "Center the Sky View on the pair selected in the list");
XtManageChild (w);
/* make the file list button */
n = 0;
w = XmCreatePushButton (pd_w, "File", args, n);
XtAddCallback (w, XmNactivateCallback, c_flist_cb, 0);
set_xmstring(w, XmNlabelString, "List to file...");
wtip (w, "Save the current list to a file");
XtManageChild (w);
/* add a separator */
n = 0;
w = XmCreateSeparator (pd_w, "Sep3", args, n);
XtManageChild (w);
/* add the close button */
n = 0;
w = XmCreatePushButton (pd_w, "Close", args, n);
XtAddCallback (w, XmNactivateCallback, c_close_cb, 0);
wtip (w, "Close this window");
XtManageChild (w);
/* make the options pulldown */
n = 0;
pd_w = XmCreatePulldownMenu (mb_w, "OptionsPD", args, n);
n = 0;
XtSetArg (args[n], XmNsubMenuId, pd_w); n++;
XtSetArg (args[n], XmNmnemonic, 'O'); n++;
cb_w = XmCreateCascadeButton (mb_w, "OptionsCB", args, n);
set_xmstring (cb_w, XmNlabelString, "Options");
XtManageChild (cb_w);
/* make the auto update button */
n = 0;
XtSetArg (args[n], XmNvisibleWhenOff, True); n++;
autoup_w = XmCreateToggleButton (pd_w, "AutoUp", args, n);
set_xmstring(autoup_w, XmNlabelString, "Auto run");
wtip (autoup_w, "Whether to automatically perform a new scan after each Main menu Update.");
XtManageChild (autoup_w);
sr_reg (autoup_w, NULL, closecategory, 1);
/* make the auto list button */
n = 0;
XtSetArg (args[n], XmNvisibleWhenOff, True); n++;
autols_w = XmCreateToggleButton (pd_w, "AutoList", args, n);
set_xmstring(autols_w, XmNlabelString, "Auto list");
wtip (autols_w,
"Whether to automatically append each new scan to the list file.");
XtManageChild (autols_w);
sr_reg (autols_w, NULL, closecategory, 1);
/* make the button to omit fixed pairs */
n = 0;
XtSetArg (args[n], XmNvisibleWhenOff, True); n++;
wander_w = XmCreateToggleButton (pd_w, "OmitFixedPairs", args, n);
set_xmstring(wander_w, XmNlabelString, "Omit fixed pairs");
wtip (wander_w, "Whether pairs of Fixed objects are not shown.");
XtManageChild (wander_w);
sr_reg (wander_w, NULL, closecategory, 1);
/* make the button to omit planets and their own satellites */
n = 0;
XtSetArg (args[n], XmNvisibleWhenOff, True); n++;
ownmoons_w = XmCreateToggleButton (pd_w, "OmitOwnMoons", args, n);
set_xmstring(ownmoons_w, XmNlabelString, "Omit planet's own moons");
wtip (ownmoons_w,
"Whether to skip pairs that are a planet and it's own moons.");
XtManageChild (ownmoons_w);
sr_reg (ownmoons_w, NULL, closecategory, 1);
/* make the help pulldown */
n = 0;
pd_w = XmCreatePulldownMenu (mb_w, "HelpPD", args, n);
n = 0;
XtSetArg (args[n], XmNsubMenuId, pd_w); n++;
XtSetArg (args[n], XmNmnemonic, 'H'); n++;
cb_w = XmCreateCascadeButton (mb_w, "HelpCB", args, n);
set_xmstring (cb_w, XmNlabelString, "Help");
XtManageChild (cb_w);
set_something (mb_w, XmNmenuHelpWidget, (XtArgVal)cb_w);
for (i = 0; i < XtNumber(helpon); i++) {
HelpOn *hp = &helpon[i];
str = XmStringCreate (hp->label, XmSTRING_DEFAULT_CHARSET);
n = 0;
XtSetArg (args[n], XmNlabelString, str); n++;
XtSetArg (args[n], XmNmarginHeight, 0); n++;
w = XmCreatePushButton (pd_w, "Help", args, n);
XtAddCallback (w, XmNactivateCallback, c_helpon_cb,
(XtPointer)(hp->key));
XtManageChild (w);
XmStringFree(str);
}
/* make the separation label and entry field */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, mb_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNleftPosition, 20); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNrightPosition, 47); n++;
XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
w = XmCreateLabel (cform_w, "SepLabel", args, n);
set_xmstring (w, XmNlabelString, "Max Sep (degs): ");
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, mb_w); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNrightPosition, 80); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNleftPosition, 53); n++;
XtSetArg (args[n], XmNcolumns, 10); n++;
sep_w = XmCreateTextField (cform_w, "Sep", args, n);
wtip (sep_w, "Enter desired max separation to list");
XtManageChild (sep_w);
/* make the limiting mag label and entry field */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, sep_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNleftPosition, 20); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNrightPosition, 47); n++;
XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
w = XmCreateLabel (cform_w, "MagLabel", args, n);
set_xmstring (w, XmNlabelString, "Mag limit: ");
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, sep_w); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNrightPosition, 80); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNleftPosition, 53); n++;
XtSetArg (args[n], XmNcolumns, 10); n++;
mag_w = XmCreateTextField (cform_w, "Mag", args, n);
wtip (mag_w, "Enter desired limiting magnitude");
XtManageChild (mag_w);
/* make a separator */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, mag_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNseparatorType, XmDOUBLE_LINE); n++;
s_w = XmCreateSeparator (cform_w, "Sep1", args, n);
XtManageChild (s_w);
/* make the list count label */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, s_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNalignment, XmALIGNMENT_CENTER); n++;
count_w = XmCreateLabel (cform_w, "Count", args, n);
set_xmstring (count_w, XmNlabelString, "0 Pairs");
XtManageChild (count_w);
/* make the time stamp label at the bottom */
n = 0;
XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
timestmp_w = XmCreateLabel (cform_w, "TSL", args, n);
set_xmstring (timestmp_w, XmNlabelString, " -- ");
wtip (timestmp_w, "Date and Time for which data are computed");
XtManageChild (timestmp_w);
/* make the list between the count and the time */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, count_w); n++;
XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNbottomWidget, timestmp_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftOffset, 10); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightOffset, 10); n++;
XtSetArg (args[n], XmNselectionPolicy, XmBROWSE_SELECT); n++;
XtSetArg (args[n], XmNlistSizePolicy, XmCONSTANT); n++;
list_w = XmCreateScrolledList (cform_w, "List", args, n);
XtAddCallback (list_w, XmNdefaultActionCallback, c_actlist_cb, NULL);
wtip (list_w, "The list of close pairs");
XtManageChild (list_w);
}
/* called when the general help key is pressed */
/* ARGSUSED */
static void
c_help_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
static char *msg[] = {
"Select desired max separation and viewpoint, then Control->Start to find all",
"pairs of objects separated by less than the given angular distance.",
};
hlp_dialog ("CloseObjects", msg, XtNumber(msg));
}
/* called when any of the individual help entries are selected.
* client contains the string for hlp_dialog().
*/
/* ARGSUSED */
static void
c_helpon_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
hlp_dialog ((char *)client, NULL, 0);
}
/* callback from file List control button. */
/* ARGSUSED */
static void
c_flist_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
XtManageChild (flist_w);
}
/* called when our toplevel shell is popped down */
/* ARGSUSED */
static void
c_popdown_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
if (XtIsManaged(flist_w))
XtUnmanageChild (flist_w);
}
/* called when the Close pushbutton is activated */
/* ARGSUSED */
static void
c_close_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
/* let the popdown do the rest of the work */
XtPopdown (cshell_w);
}
/* called when a list item is double-clicked */
/* ARGSUSED */
static void
c_actlist_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
(void) sky_point();
}
/* called when the Sky Point pushbutton is activated */
/* ARGSUSED */
static void
c_skypoint_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
if (sky_point() < 0)
xe_msg (1, "First select a line from the list.");
}
/* called when the Perform Search pushbutton is activated */
/* ARGSUSED */
static void
c_go_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
do_search();
}
/* tell sky view to point to the first object named in the current selection.
* return 0 if ok, else -1.
*/
static int
sky_point()
{
String sel;
char objname[MAXNM];
XmStringTable si;
int sic;
DBScan dbs;
Obj *op;
int i;
/* get the current select item, if any */
get_something (list_w, XmNselectedItemCount, (XtArgVal)&sic);
if (sic < 1)
return (-1);
get_something (list_w, XmNselectedItems, (XtArgVal)&si); /* don't free*/
XmStringGetLtoR (si[0], XmSTRING_DEFAULT_CHARSET, &sel); /* free */
/* extract the name of the first object -- see dspl_pairs's formating */
(void) strncpy (objname, sel+7, sizeof(objname));
XtFree (sel);
objname[sizeof(objname)-1] = '\0';
for (i = MAXNM-2; i >= 0; --i)
if (objname[i] == ' ')
objname[i] = '\0';
else
break;
/* scan the db for the object */
for (db_scaninit (&dbs, ALLM, NULL, 0); (op = db_scan(&dbs)) != NULL; )
if (strcmp (op->o_name, objname) == 0)
break;
/* point sky if found */
if (op)
sv_point (op);
else
xe_msg (1, "Can not find `%s' in memory!", objname);
return (0);
}
/* given two pointers to Obj *, return how they sort by dec in qsort-style.
*/
static int
racmp_f (const void *v1, const void *v2)
{
Obj *op1 = *((Obj **)v1);
Obj *op2 = *((Obj **)v2);
double ra1 = op1->s_ra;
double ra2 = op2->s_ra;
if (ra1 < ra2)
return (-1);
if (ra1 > ra2)
return (1);
return (0);
}
static void
do_search()
{
int als = XmToggleButtonGetState(autols_w);
double sep; /* desired max separation, rads */
double mag; /* limiting magnitude */
int wander; /* 1 to limit list to at least one wanderer */
int ownmoons; /* 1 to omit planet's own moons */
Band *bp = NULL;/* malloced array of nb bands */
int nb; /* number of dec (or alt) bands */
Pair *pp = NULL;/* malloced list of close pairs */
int np; /* number of pairs */
/* retrieve user option settings */
if (get_options (&sep, &mag, &wander, &ownmoons) < 0)
return;
/* ok, let's get to the real work */
watch_cursor (1);
/* compute band size, deal and sort objects into their own and adjacent
* bands.
*/
nb = init_bands (sep, mag, &bp);
if (nb < 0) {
watch_cursor (0);
return;
}
/* look through bands for all pairs closer than sep.
* form sorted lists of them in malloced pp array.
*/
np = find_close (bp, nb, sep, wander, ownmoons, &pp);
/* finished with the bands now */
free_band (bp, nb);
/* report what we found */
if (np > 0)
(void) dspl_pairs (pp, np, sep);
else if (!als)
xe_msg (1, "No such pairs found.");
/* add to file if on */
if (als)
flistok_append_cb ();
/* finished */
if (pp)
free ((char *)pp);
watch_cursor (0);
}
/* retrieve the desired separation and other user option settings.
* return 0 if ok, else -1
*/
static int
get_options (sp, mp, wp, op)
double *sp; /* max separation, rads */
double *mp; /* limiting magnitude */
int *wp; /* wanderer flag */
int *op; /* ownmoons flag */
{
char *str;
str = XmTextFieldGetString (sep_w);
*sp = degrad(atod(str));
XtFree (str);
if (*sp < MINSEP) {
xe_msg (1, "Please specify a max separation >= %g degrees.",
raddeg(MINSEP));
return (-1);
}
str = XmTextFieldGetString (mag_w);
*mp = atod(str);
XtFree (str);
*wp = XmToggleButtonGetState (wander_w);
*op = XmToggleButtonGetState (ownmoons_w);
return (0);
}
/* free a list of nb Bands, including their opp.
* allow for NULL pointers at any step.
*/
static void
free_band (bp, nb)
Band bp[];
int nb;
{
Band *bpsav = bp;
if (!bp)
return;
for (bp += nb; --bp >= bpsav; )
if (bp->opp)
free ((char *)bp->opp);
free ((char *)bpsav);
}
/* given separation and other factors, build up the array of bands:
* find the number of bands and malloc array of bands at *bpp;
* deal each qualified object into its own and adjacent-up band;
* sort each band according to increasing ra (or az).
* if all ok return number of bands created at *bpp, else -1.
* N.B. caller must free_band(*bpp) if we return >= 0.
*/
static int
init_bands (sep, mag, bpp)
double sep;
double mag;
Band **bpp;
{
#define BAND(x) ((int)floor(((x)+PI/2)/bandsiz))
int topo = pref_get (PREF_EQUATORIAL) == PREF_TOPO;
double bandsiz;
Band *bp;
DBScan dbs;
int nobj;
int nb;
Obj *op;
int n, i;
/* compute number of bands and band size */
nb = (int)ceil(PI/sep);
if (nb < 1) {
xe_msg (1, "Please decrease max separation to < 180 degrees.");
return (-1);
}
bandsiz = PI/nb;
/* malloc list of zero'd Bands */
bp = (Band *) calloc (nb, sizeof(Band));
if (!bp) {
xe_msg (1,
"Could not malloc %d bands -- try increasing max separation.",
nb);
return(-1);
}
/* scan db and fill in bands.
* each object goes in its own band and the one adjacent.
* we do not include undefined user objects nor those too dim or low.
* we also check for entries with bogus bands but should never be any.
*/
nobj = db_n();
n = 0;
for (db_scaninit(&dbs, ALLM, NULL, 0); (op = db_scan(&dbs)) != NULL; ) {
int b;
pm_set (33 * n++ / nobj); /* about 0-33% of time here */
/* update object */
db_update (op);
if (get_mag(op) > mag)
continue;
if (topo && op->s_alt < 0.0)
continue;
/* find which band op fits into */
b = BAND(op->s_dec);
if (b < 0 || b > nb) {
xe_msg (0, "%s is out of band: %d.", op->o_name, b);
continue;
}
/* add op to its band */
if (add_obj (&bp[b], op) < 0) {
free_band (bp, nb);
return (-1);
}
/* unless we are at the top (N pole) add op to next band up too */
if (b < nb-1 && add_obj (&bp[b+1], op) < 0) {
free_band (bp, nb);
return (-1);
}
}
/* sort each band by ra (az)
*/
for (i = 0; i < nb; i++)
if (bp[i].nuse > 0)
qsort ((void *)bp[i].opp, bp[i].nuse, sizeof (Obj *), racmp_f);
/* report answers and return */
*bpp = bp;
return (nb);
}
/* add one more op to Band bp, growing opp as necessary.
* return 0 if ok, else xe_msg() and -1.
*/
static int
add_obj (bp, op)
Band *bp;
Obj *op;
{
#define BCHUNKS 32 /* grow the Obj * list at opp this much at a time */
/* init the list if this is the first time. */
if (!bp->opp) {
char *newmem;
newmem = malloc (BCHUNKS * sizeof(Obj *));
if (!newmem) {
xe_msg (1, "Can not malloc temp Objects.");
return (-1);
}
bp->opp = (Obj **) newmem;
bp->nmem = BCHUNKS;
bp->nuse = 0;
}
/* grow the list if necessary */
if (bp->nuse == bp->nmem) {
char *newmem;
newmem = realloc((char *)bp->opp, (bp->nmem+BCHUNKS)*sizeof(Obj *));
if (!newmem) {
xe_msg (1, "Can not realloc more temp Objects.");
return (-1);
}
bp->opp = (Obj **) newmem;
bp->nmem += BCHUNKS;
}
bp->opp[bp->nuse++] = op;
return (0);
}
/* malloc and fill *ppp with the close pairs in the sorted bands.
* return count of new items in *ppp if ok else -1.
* N.B. caller must free *ppp if it is != NULL no matter what we return.
*/
static int
find_close (bp, nb, maxsep, wander, ownmoons, ppp)
Band bp[]; /* list of bands to scan */
int nb; /* number of bands */
double maxsep; /* max sep we are looking for, rads */
int wander; /* 1 if limit to pairs with at least one wanderer (ss object) */
int ownmoons; /* 1 if limit planets with their own moons */
Pair **ppp; /* return list of pairs we malloc */
{
double stopsep = maxsep*2.3; /* stop scan at maxsep*sqrt(1**2 + 2**2) */
int m = 0; /* n pairs there are room for in *ppp */
int n = 0; /* n pairs in use in *ppp */
int b;
/* be sure everyone knows nothing has been malloced yet */
*ppp = NULL;
/* scan each band */
for (b = 0; b < nb; b++, bp++) {
int nuse = bp->nuse;
int i;
pm_set (33 + 33 * b / nb); /* about 33-66% more of time here */
/* compare against each object in this band */
for (i = 0; i < nuse; i++) {
Obj *opi = bp->opp[i];
int j;
/* set up for finding sep -- see solve_sphere() -- i is c */
double cc = sin(opi->s_dec);
double sc = cos(opi->s_dec);
/* scan forward for close objects in the same band.
* wrap, but not so far as to reach self again.
* since bands are sorted we can cut off when find one too far.
* but this _can_ lead to dups for very short band lists.
*/
for (j = (i+1)%nuse; j != i; j = (j+1)%nuse) {
Obj *opj = bp->opp[j];
double cb, sb;
double A, cA;
double sep, csep;
/* omit if both are fixed and want at least one wanderer */
if (wander && is_type(opi,FIXEDM) && is_type(opj,FIXEDM))
continue;
/* omit if both are in the same planet system */
if (ownmoons && is_type(opi,PLANETM) && is_type(opj,PLANETM)
&& opi->pl_code == opj->pl_code)
continue;
/* find the exact separation */
cb = sin(opj->s_dec); /* j is b */
sb = cos(opj->s_dec);
A = opi->s_ra - opj->s_ra;
cA = cos(A);
csep = cb*cc + sb*sc*cA;
if (csep > 1.0) csep = 1.0; /* just paranoid */
if (csep < -1.0) csep = -1.0;
sep = acos(csep);
/* stop when we're so far from opi there can be no more */
if (sep > stopsep)
break;
if (sep <= maxsep)
if (add_pair (opi, opj, sep, ppp, &m, &n) < 0)
return (-1);
}
}
}
return (n);
}
/* add the two objects to the list of pairs.
* we malloc/realloc as needed to grow *ppp, updating {m,n}p.
* put the smaller of op1/2 in slot op1 for later dup checking.
* return 0 if ok, else -1.
* N.B. the caller must free *ppp if it is not NULL no matter what we return.
*/
static int
add_pair (op1, op2, sep, ppp, mp, np)
Obj *op1, *op2; /* the two objects to report */
double sep; /* separation, rads */
Pair **ppp; /* pointer to malloced list of lines */
int *mp; /* pointer to number of Pairs already in *ppp */
int *np; /* pointer to number of *ppp actually in use */
{
#define PCHUNKS 32 /* grow the report list this many at a time */
Pair *newp;
/* init the list if this is the first time. */
if (!*ppp) {
char *newmem;
newmem = malloc (PCHUNKS * sizeof(Pair));
if (!newmem) {
xe_msg (1, "Can not malloc any Pairs.");
return (-1);
}
*ppp = (Pair *) newmem;
*mp = PCHUNKS;
*np = 0;
}
/* grow the list if necessary */
if (*np == *mp) {
char *newmem;
newmem = realloc((char *)*ppp, (*mp+PCHUNKS) * sizeof(Pair));
if (!newmem) {
xe_msg (1, "Can not realloc more Pairs.");
return (-1);
}
*ppp = (Pair *) newmem;
*mp += PCHUNKS;
}
newp = &(*ppp)[*np];
*np += 1;
/* this lets us sort pairs of objects when their sep matches for
* later elimination of dups.
*/
if (op1 < op2) {
newp->op1 = op1;
newp->op2 = op2;
} else {
newp->op1 = op2;
newp->op2 = op1;
}
newp->sep = (float)sep;
return (0);
}
/* sort the two Pairs according to increasing separation in qsort() fashion
* then by memory location of op1 then op2 -- this way two pairs of the same
* objects will sort adjacent, and is used later to elminate dups.
*/
static int
pair_cmpf (const void *v1, const void *v2)
{
Pair *p1 = (Pair *)v1;
Pair *p2 = (Pair *)v2;
double s1 = (double)p1->sep;
double s2 = (double)p2->sep;
if (s1 < s2)
return (-1);
else if (s1 > s2)
return (1);
else {
Obj *opa, *opb;
opa = p1->op1;
opb = p2->op1;
if (opa < opb)
return (-1);
else if (opa > opb)
return (1);
opa = p1->op2;
opb = p2->op2;
if (opa < opb)
return (-1);
else if (opa > opb)
return (1);
else
return (0);
}
}
/* fill the list widget with the given set of pairs.
* also update the attendent messages.
* return 0 if ok else -1.
*/
static int
dspl_pairs (pp, np, sep)
Pair pp[];
int np;
double sep;
{
XmString *xmstrtbl;
char buf[128];
char sepstr[64];
int ns;
int i;
/* sort pairs by increasing separation then by object pointers */
qsort ((void *)pp, np, sizeof(Pair), pair_cmpf);
/* put into the list in one big push for best performance.
* beware of and remove any dups.
* ns ends up as the number of strings in xmstrtbl[].
*/
xmstrtbl = (XmString *) malloc (np * sizeof(XmString));
if (!xmstrtbl) {
xe_msg (1, "No memory for %d XmStrings", np);
return (-1);
}
for (ns = i = 0; i < np; i++) {
Pair *ppi = &pp[i];
pm_set (66 + 29 * i / np); /* about 66-95% time here */
/* can eliminate dups this way because of sort */
if (i > 0) {
Pair *lpp = &pp[i-1];
if (ppi->op1 == lpp->op1 && ppi->op2 == lpp->op2)
continue;
}
/* N.B. if you change this format, rework sky_point's extraction */
fs_pangle (sepstr, (double)ppi->sep);
(void) sprintf (buf, "%6.2f %-*.*s %6.2f %-*.*s %s",
get_mag(ppi->op1),
MAXNM-1, MAXNM-1, ppi->op1->o_name,
get_mag(ppi->op2),
MAXNM-1, MAXNM-1, ppi->op2->o_name,
sepstr);
xmstrtbl[ns++] = XmStringCreateSimple (buf);
}
/* reload the list */
XtUnmanageChild (list_w);
XmListDeleteAllItems (list_w);
XmListAddItems (list_w, xmstrtbl, ns, 0);
XtManageChild (list_w);
/* finished with xmstrtbl */
for (i = 0; i < ns; i++)
XmStringFree (xmstrtbl[i]);
free ((char *)xmstrtbl);
/* update the messages */
fs_pangle (sepstr, sep);
(void) sprintf (buf, "Found %d %s Pair%s <=%s", ns,
pref_get(PREF_EQUATORIAL)==PREF_TOPO ? "Topocentric" : "Geocentric",
ns==1 ? "" : "s", sepstr);
set_xmstring (count_w, XmNlabelString, buf);
timestamp (mm_get_now(), timestmp_w);
pm_set (100);
return (0);
}
/* create the list filename prompt */
static void
c_create_flist_w()
{
Arg args[20];
Widget tw;
int n;
n = 0;
XtSetArg (args[n], XmNcolormap, xe_cm); n++;
XtSetArg(args[n], XmNmarginWidth, 20); n++;
XtSetArg(args[n], XmNmarginHeight, 20); n++;
flist_w = XmCreatePromptDialog (cshell_w, "CloseList", args,n);
set_something (flist_w, XmNcolormap, (XtArgVal)xe_cm);
set_xmstring (flist_w, XmNdialogTitle, "xephem Close list");
set_xmstring (flist_w, XmNselectionLabelString, "File name:");
tw = XmSelectionBoxGetChild(flist_w, XmDIALOG_TEXT);
defaultTextFN (tw, 1, "closelist.txt", NULL);
sr_reg (tw, NULL, closecategory, 1);
XtUnmanageChild (XmSelectionBoxGetChild(flist_w, XmDIALOG_HELP_BUTTON));
XtAddCallback (flist_w, XmNokCallback, c_flistok_cb, NULL);
XtAddCallback (flist_w, XmNmapCallback, prompt_map_cb, NULL);
}
/* called when the Ok button is hit in the file flist prompt */
/* ARGSUSED */
static void
c_flistok_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
char buf[1024];
int icount;
char *name;
get_xmstring(w, XmNtextString, &name);
if (strlen(name) == 0) {
xe_msg (1, "Please enter a file name.");
XtFree (name);
return;
}
get_something (list_w, XmNitemCount, (XtArgVal)&icount);
if (icount == 0) {
xe_msg (1, "No items in list -- no file action.");
XtFree (name);
return;
}
if (existd (name,NULL) == 0 && confirm()) {
(void) sprintf (buf, "%s exists:\nAppend or Overwrite?", name);
query (cshell_w, buf, "Append", "Overwrite", "Cancel",
flistok_append_cb, flistok_overwrite_cb, NULL);
} else
flistok_overwrite_cb();
XtFree (name);
}
/* called when we want to append to a flist file */
static void
flistok_append_cb ()
{
char *name;
get_xmstring (flist_w, XmNtextString, &name);
make_flist (name, "a");
XtFree (name);
}
/* called when we want to ceate a new flist file */
static void
flistok_overwrite_cb ()
{
char *name;
get_xmstring (flist_w, XmNtextString, &name);
make_flist (name, "w");
XtFree (name);
}
/* open the named flist file "a" or "w" and fill it in. */
static void
make_flist (name, how)
char *name;
char *how;
{
FILE *fp = fopend (name, NULL, how);
if (fp) {
write_flist (fp);
(void) fclose (fp);
} else {
XmToggleButtonSetState (autols_w, False, False);
XmToggleButtonSetState (autoup_w, False, False);
}
}
/* write out all objects currently in the list, with header, to fp */
static void
write_flist (fp)
FILE *fp;
{
XmStringTable items;
int icount;
char *p;
int i;
get_something (list_w, XmNitemCount, (XtArgVal)&icount);
if (icount <= 0)
return;
watch_cursor(1);
get_xmstring (count_w, XmNlabelString, &p);
(void) fprintf (fp, "%s", p);
XtFree (p);
get_xmstring (timestmp_w, XmNlabelString, &p);
(void) fprintf (fp, " at %s\n", p);
XtFree (p);
get_something (list_w, XmNitems, (XtArgVal)&items);
for (i = 0; i < icount; i++) {
XmStringGetLtoR (items[i], XmSTRING_DEFAULT_CHARSET, &p);
(void) fprintf (fp, "%s\n", p);
XtFree (p);
}
watch_cursor(0);
}