/* File: gmapdisp.c * Author: Jean Thierry-Mieg (mieg@mrc-lmb.cam.ac.uk) * Copyright (C) J Thierry-Mieg and R Durbin, 1991 * ------------------------------------------------------------------- * Acedb is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------- * This file is part of the ACEDB genome database package, written by * Richard Durbin (MRC LMB, UK) rd@mrc-lmb.cam.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * Description: Display of the genetic map * Exported functions: * gMapDisplay * gMapMakeAll * gMapHighlightKey * gMapUnselect * gMapColourBox * gMapOrder * gMapConvert * HISTORY: * Last edited: Apr 12 14:47 2002 (edgrif) * CVS info: $Id: gmapdisp.c,v 1.82 2002/04/16 13:06:20 edgrif Exp $ *------------------------------------------------------------------- */ #include #include #include #include /* for checkWriteAccess() */ #include #include /* for controlMakeColumns() */ magic_t GeneticMap_MAGIC = "GeneticMap"; static COLPROTO columnsTab[] = { &gMapMainMarkersColumn, &gMapMiniChromBandsColumn, &mapLocatorColumn, &mapScrollerColumn, &gMapJTMIntervalColumn, &gMapRDIntervalColumn, &gMapChromBandsColumn, &gMapContigColumn, &gMapRevPhysColumn, &gMapPhysGenesColumn, &mapScaleColumn, &gMapTwoPtColumn, &gMapMultiPtColumn, &gMapLikelihoodColumn, &gMapPointColumn, &gMapRemarkColumn, &spacerColumn, &gMapPosNegColumn, &gMapSubMapColumn, 0 }; static COLDEFAULT defaultColumns = { { &gMapMainMarkersColumn, TRUE, "Marker_loci" }, { &gMapMiniChromBandsColumn, TRUE, "Chromsome_picture" }, { &mapScrollerColumn, FALSE, "Scroller" }, { &mapLocatorColumn, TRUE, "Locator" }, { &gMapJTMIntervalColumn, TRUE, "Intervals" }, { &gMapContigColumn, TRUE, "Contigs" }, { &gMapRevPhysColumn, TRUE, "Reversed_physical" }, { &gMapPhysGenesColumn, TRUE, "Physical_genes" }, { &mapScaleColumn, TRUE, "Scale" }, { &gMapTwoPtColumn, FALSE, "Two_point_data" }, { &gMapMultiPtColumn, FALSE, "Multi_point_data" }, { &gMapLikelihoodColumn, FALSE, "Likelihood_distribution" }, { &gMapPointColumn, TRUE, "Loci" }, { &gMapSubMapColumn, FALSE, "submap" }, { 0, 0, 0} }; /************************************************************/ GeneticMap currentGeneticMap (char *caller) { GeneticMap gMap; gMap = (GeneticMap)(currentMapControl())->look; if (!gMap) messcrash("%s() received NULL GeneticMap pointer", caller); if (gMap->magic != &GeneticMap_MAGIC) messcrash("%s() received non-magic GeneticMap pointer", caller); return gMap; } /* currentGeneticMap */ void gMapMakeAll (void) { KEY chromosome = 0; BOOL isold = FALSE; Graph g = 0; if (!isWriteAccess ()) { messout ("Sorry, you do not have Write Access") ; return ; } while (lexNext (_VgMap,&chromosome)) arrayKill(chromosome); chromosome = 0; while (lexNext (_VMap,&chromosome)) { if (isold) g = graphActive(); isold = gMapDisplay(chromosome, 0, isold, NULL); if (g) graphActivate(g); if (messIsInterruptCalled ()) break ; } if (g) { graphActivate(g); graphDestroy(); } } /********************************/ /* Main menu */ /********************************/ static void flip(void) { MAPCONTROL map = currentMapControl(); map->mag = -map->mag; controlDraw(); } static void gMapGotoCmap (void) { MAPCONTROL map = currentMapControl() ; display (map->key, 0, "CMAP") ; } static void gMapRecalculate(void) { MAPCONTROL map = currentMapControl() ; KEY key ; float cen = map->centre, mag = map->mag ; /* If multi map then parentKey will be set, else it won't*/ if(map->parentKey) key = map->parentKey; else key= map->key; /* destroy a cached set of segs */ gMapCacheKill(key); map->key = 0 ; gMapDisplay (key, 0, TRUE, NULL) ; /* reuse the graph */ map = currentMapControl() ; map->centre = cen ; map->mag = mag ; controlDraw() ; } static void gMapSaveMap (void) { GeneticMap look = currentGeneticMap("gMapSaveMap"); if (!checkWriteAccess()) return ; gMapSaveDetails (look->segs, look->map->key) ; } static void gMapSaveMapPrivate (void) { KEY key; char *cp , buffer[41] ; OBJ obj ; MAPCONTROL map = currentMapControl(); GeneticMap look = currentGeneticMap("gMapSaveMapPrivate"); ACEIN name_in; if (!checkWriteAccess()) return ; if (!(name_in = messPrompt ("Save as :", name(map->key), "t", 0))) return ; strncpy(buffer, aceInWord(name_in), 40) ; buffer[40] = '\0'; aceInDestroy(name_in); if (lexword2key(buffer, &key, _VMap) && !messQuery ("Overwrite existing map ?")) return ; lexaddkey (buffer, &key, _VMap) ; if (!key) return ; if ((obj = bsCreate(key))) { if (!bsFindTag(obj, _Author) || /* as in the official chromo */ (bsGetData(obj, _Author, _Text, &cp) && strcmp(cp, thisSession.name)) ) { messout ("This map is not yours, save it under some other name, sorry") ; bsDestroy(obj) ; return ; } bsDestroy(obj) ; } if (key != map->key) { obj = bsUpdate(key) ; bsAddKey(obj, _From_map, map->key) ; bsAddData(obj, _Author, _Text, thisSession.name) ; bsSave(obj) ; } gMapSaveDetails (look->segs, key) ; return; } /* gMapSaveMapPrivate */ static void setMagnification (void) { MAPCONTROL map = currentMapControl(); float old = map->mag ; /* avoid zero, mhmp, dec 98 */ ACEIN mag_in; if ((mag_in = messPrompt ("Give new magnification", messprintf ("%.2f", map->mag), "fz", 0))) { aceInFloat (mag_in, &map->mag) ; aceInDestroy (mag_in); } if (!map->mag) map->mag = old ; else controlDraw () ; return; } /* setMagnification */ static void removeThisMap(void) { controlDeleteMap(currentColControl("removeThisMap"), currentMapControl()); } static void hideHeaderToggle() { MAPCONTROL map = currentMapControl(); COLCONTROL control = map->control; control->hideHeader = !control->hideHeader; controlDraw () ; } static MENUOPT gMapMenu[] = { { graphDestroy, "Quit" }, { help, "Help" }, { graphPrint, "Print Screen" }, { controlPrint,"Print Whole Map" }, { menuSpacer, "" }, { removeThisMap, "Close Current Map" }, { setMagnification, "Set Magnification" }, { displayPreserve, "Preserve" }, { gMapRecalculate, "Recalculate" }, { flip, "Vertical Flip" }, { gMapSaveMap, "Save Map" }, { gMapSaveMapPrivate, "Private Save" }, { gMapGotoCmap, "Physical Chromo Map" }, { hideHeaderToggle, "Hide Header" }, { 0, 0 } } ; /*************************************************************/ /******** gMap show intersect with active keyset *************/ /*************************************************************/ static void gMapClear (void) { GeneticMap look = currentGeneticMap("gMapClear"); look->highlight = keySetReCreate(look->highlight); look->hidden = keySetReCreate(look->hidden); controlDraw(); /* deals with stressed et al */ return; } /* gMapClear */ static void gMapHighlightAll (void) { int i ; GeneticMap look = currentGeneticMap("gMapHighlightAll"); look->highlight = keySetReCreate(look->highlight); for (i = 0 ; i < arrayMax(look->segs) ; ++i) keySet(look->highlight, keySetMax(look->highlight)) = arrp(look->segs,i,GMAPSEG)->key; keySetSort(look->highlight); keySetCompress(look->highlight); controlUnselectAll(); } static void gMapHighlight (void) { KEYSET kSet = 0 ; GeneticMap look = currentGeneticMap("gMapHighlight"); if (!keySetActive(&kSet, 0)) { messout("First select a keySet window, thank you.") ; return ; } keySetDestroy(look->highlight); look->highlight = keySetCopy(kSet); keySetSort(look->highlight); controlDraw(); } static void gMapUnHighlight (void) { KEYSET kSet = 0 ; KEYSET tmp; GeneticMap look = currentGeneticMap("gMapUnHighlight"); if (!keySetActive(&kSet, 0)) { messout("First select a keySet window, thank you.") ; return ; } tmp = look->highlight; look->highlight = keySetMINUS(tmp, kSet); keySetDestroy(tmp); controlDraw(); } static void gMapExportHighlit (void) { KEYSET kset ; GeneticMap look = currentGeneticMap("gMapExportHighlit"); kset = keySetCopy(look->highlight) ; keySetNewDisplay (kset, "Exported KeySet"); } static void gMapHideHighlit (void) { GeneticMap look = currentGeneticMap("gMapHideHighlit"); KEYSET tmp; tmp = look->hidden; look->hidden = keySetOR(look->hidden, look->highlight); keySetDestroy(tmp); controlDraw() ; } static void gMapHideUnHighlit (void) { int i ; GeneticMap look = currentGeneticMap("gMapHideUnHighlit"); KEYSET a = keySetCreate (), b, c ; for (i = 0 ; i < arrayMax(look->segs) ; ++i) keySet(a, keySetMax(a)) = arrp(look->segs,i,GMAPSEG)->key; keySetSort(a) ; keySetCompress(a) ; b = keySetMINUS(a,look->highlight) ; c = look->hidden ; look->hidden = keySetOR(b, c); keySetDestroy(a) ; keySetDestroy(b) ; keySetDestroy(c) ; controlDraw() ; return; } /* gMapHideUnHighlit */ static void gMapUnHide (void) { GeneticMap look = currentGeneticMap("gMapUnHide"); look->hidden = keySetReCreate(look->hidden); controlDraw(); return; } /* gMapUnHide */ void gMapHighlightKey(MAPCONTROL map, KEY key) /* public routine */ { GeneticMap look = (GeneticMap)(map->look); keySet(look->highlight, keySetMax(look->highlight)) = key; keySetSort(look->highlight); keySetCompress(look->highlight); controlUnselectAll(); /* So that object is magenta, not cyan after redraw */ controlDrawControl(map->control); return; } /* gMapHighlightKey */ static MENUOPT clearMenu[] = { { gMapClear, "Clear and show all" }, { gMapHighlightAll, "Highlight all" }, { gMapHighlight, "Highlight selected keyset" }, { gMapUnHighlight, "Unhighlight selected keyset" }, { gMapHideHighlit, "Hide highlit items" }, { gMapHideUnHighlit, "Hide unhighlit items" }, { gMapUnHide, "Unhide hidden items" }, { gMapExportHighlit, "Export highlit keyset" }, { 0, 0 } } ; static COLOUROPT highlightButton[] = { { (ColouredButtonFunc)gMapHighlight, 0, BLACK, WHITE, "Highlight...", 0 } }; /* Note, as the button to which the above Menu is attached is only drawn when it is the current map (it's in the header), we can rely on currentMap() to get us our map. */ /**************************************************/ /* gMap display code */ /**************************************************/ static void gMapFinalise(void *p) { int i ; MAPCONTROL map = (MAPCONTROL)p; GeneticMap look = (GeneticMap)(map->look); if (isWriteAccess()) for (i = 0 ; i < arrayMax(look->segs) ; ++i) if (arrp(look->segs,i,GMAPSEG)->flag & FLAG_MOVED) { if (messQuery ("Do you want to save your changes?")) gMapSaveDetails (look->segs, map->key) ; break ; } /* destroy in order of declaration in gmap.h */ /* destroyed on map handle keySetDestroy(look->orderedLoci) ; assDestroy (look->posAss); */ keySetDestroy(look->highlight); keySetDestroy(look->hidden); keySetDestroy(look->positive); keySetDestroy(look->negative); keySetDestroy(look->neighbours); keySetDestroy(look->twoPt); keySetDestroy(look->multi); /* keySetDestroy(look->negativeMarkers); never allocated directly */ arrayDestroy (look->segs); arrayDestroy (look->remarkSegs); look->magic = 0 ; messfree(look); } static void gMapKybd (int k) { MAPCONTROL map = currentMapControl(); switch (k) { case UP_KEY : if (map->cursor.box) { if (map->mag > 0) --map->cursor.val ; else ++map->cursor.val ; mapControlCursorShift (map) ; } break ; case DOWN_KEY : if (map->cursor.box) { if (map->mag > 0) ++map->cursor.val ; else --map->cursor.val ; mapControlCursorShift (map) ; } break ; } } static float gMapDrawHeader (MAPCONTROL map) { COLCONTROL control = map->control; GeneticMap look = (GeneticMap)(map->look); int x ; float y ; *look->messageText = 0; look->aboutBox = graphBoxStart(); graphTextFormat (BOLD) ; if (look->titleKey) { x = strlen (name(look->titleKey)) ; graphText (name(look->titleKey),1,0.5) ; } else { x = strlen (name(map->key)) ; graphText (name(map->key),1,0.5) ; } graphTextFormat (PLAIN_FORMAT) ; graphBoxEnd(); if (map->noButtons) /* may suppress header buttons for WWW */ y = 1.4; else y = controlDrawButtons (map, x+2, 0.5, control->graphWidth) ; look->messageBox = graphBoxStart () ; graphTextPtr (look->messageText, 1, y + 0.25, MAXMESSAGETEXT-1) ; graphBoxEnd () ; graphBoxDraw (look->messageBox, BLACK, PALECYAN) ; return y + 1.5 ; } void gMapAddToHeader(MAPCONTROL map, char *string) { char *mt = ((GeneticMap)map->look)->messageText; strncat(mt, string, strlen(mt)look); if (box == look->aboutBox) display(map->key, 0, "TREE"); } static BOOL getCentre(KEY from, KEY map, float *ret) { if (class(from) == _VCalcul) { *ret = (float)(KEYKEY(from))/1000.0 - 1000; return TRUE; } if (gMapGetMapObject(from, map, 0, 20, ret, 0, 0, 0)) return TRUE; else return FALSE; } static MAPCONTROL gMapDoMap(COLCONTROL control, KEY key, KEY from, KEY view, int nmaps) { MAPCONTROL map = mapControlCreate (control, gMapFinalise) ; GeneticMap look ; float centre; OBJ obj; /* look freed in gMapFinalise to make sure it's around when needed */ look = (GeneticMap) messalloc (sizeof (struct GeneticMapStruct)); map->look = (void *)look; look->map = map; /* how incestuous! */ look->magic = &GeneticMap_MAGIC; look->highlight = keySetCreate(); look->hidden = keySetCreate(); look->positive = keySetCreate(); look->negative = keySetCreate(); look->neighbours = keySetCreate(); look->multi = keySetCreate(); look->twoPt = keySetCreate(); /* messalloc automatically initialise to zero look->negativeMarkers = 0; look->posAss = 0; look->titleKey = 0; look->remarkSegs = 0; look->segs = 0; */ if ((obj = bsCreate(key))) { bsGetKey(obj, _Title, &look->titleKey) ; bsDestroy(obj) ; } map->displayName = "GMAP"; if(nmaps ==1){ map->name = strnew(name(key), map->handle); map->parentKey = 0; } else{ map->name = strnew(name(from), map->handle); map->parentKey = from; } map->key = key; map->prototypes = columnsTab; map->menu = gMapMenu; map->drawHeader = gMapDrawHeader; map->headerPick = gMapHeaderPick; map->keyboard = gMapKybd; map->topMargin = 3; map->viewTag = str2tag("Gmap"); controlAddMap(control, map); getPos(map, 0, 0); /* init map->min, map->max, map->mag, map->centre + position associator */ controlConvertPerm(map); /* call all the conversion routines */ /* make the initial set of columns */ controlMakeColumns(map, defaultColumns, view, nmaps>1); /* A button which don't really belong to any column, so we tack them on the end here */ controlAddButton(map, highlightButton, clearMenu); if (getCentre(from, map->key, ¢re)) { map->centre = centre ; mapControlCursorSet (map, centre) ; } else if (control->hideHeader) /* if hideheader set in the view default to displaying it all */ { BOOL isNeg; map->centre = (map->max + map->min) / 2.0 ; isNeg = (map->mag < 0) ; map->mag = (map->control->graphHeight - 2.0) / (map->max - map->min) ; if (isNeg) map->mag = -map->mag ; } return map ; } /* display newMap in a new MAP on the current control, if not shown already, and centre and size correctly */ BOOL gMapFollowMap (COLCONTROL control, MAPCONTROL oldMap, KEY newMapKey) { float x, dx, x1, x2 ; float y1, y2 ; int i ; MAPCONTROL newMap ; /* do we have it already? */ for (i=0; i < arrayMax(control->maps); i++) { newMap = arr(control->maps, i, MAPCONTROL); if (newMap->key == newMapKey) break ; } /* if not, make a new map */ if (i == arrayMax(control->maps)) if (!(newMap = gMapDoMap (control, newMapKey, 0, 0, 2))) return FALSE ; /* 2 forces minimal-view */ /* get ends on current map */ if ((gMapGetMapObject (newMapKey, oldMap->key, 0, 20, &x, &dx, 0, 0) != FLAG_ANY_INTERVAL) || !dx) return FALSE ; x1 = x - dx ; x2 = x + dx ; /* extent in its own coords */ y1 = newMap->min ; y2 = newMap->max ; /* new centre, mag */ newMap->centre = y1 + (oldMap->centre - x1) * (y2 - y1) / (x2 - x1) ; newMap->mag = oldMap->mag * (x2 - x1) / (y2 - y1) ; controlDraw () ; return TRUE ; } static Array allWindows = 0; static void gMapControlRemove(void *cp) { int i; COLCONTROL c = *((COLCONTROL *)cp); for ( i=0; imaps) != keySetMax(maps)) continue; /* different number, failed */ for (j=0; jmaps); j++) { MAPCONTROL m = arr(c->maps, j, MAPCONTROL); if (!keySetFind(maps, m->key, 0)) break; if (assFind(views, assVoid(m->key), 0)) break; } if (j == arrayMax(c->maps)) break; /* found the match */ } if (i != arrayMax(allWindows)) /* found match */ { float centre; control = arr(allWindows, i, COLCONTROL); graphActivate(control->graph); for (i=0; imaps); i++) { MAPCONTROL map = arr(control->maps, i, MAPCONTROL); if (getCentre(from, map->key, ¢re)) { map->centre = centre; mapControlCursorSet(map, centre); } } haveControl = TRUE ; } } if (!haveControl) /* Stage three make a new control, in new or existing window, then display the maps in it */ { Stack buff = stackCreate (127) ; COLCONTROL *cp; for (i=0; ihandle, sizeof(COLCONTROL)); *cp = control; for (i=0; ifrom = from; controlDraw(); keySetDestroy(maps); assDestroy(views); return TRUE; } /*********************************************************************/ /* Some routines shared by the locus chrom_band and interval columns */ /*********************************************************************/ void gMapUnselect(MAPCONTROL map) /* this avoids every column having to know about all map-global highlighting data, even if it's in fact used only between a couple of columns. This function unsets everything that's shared in the map */ { GeneticMap look = (GeneticMap)map->look; /* look->selectKey is always valid */ look->neighboursInfoValid = FALSE; /* look->neighbours valid when true */ look->friendsInfoValid = FALSE; /* look->positive, negative */ look->dataInfoValid = FALSE; /* look_multi, 2pt */ look->coOrdsInvalid = FALSE; look-> negativeMarkersValid = FALSE; /* valid when submap contig picked */ } BOOL gMapIsNeighbour(MAPCONTROL map, KEY key) { GeneticMap look = (GeneticMap)map->look; if (key == map->control->activeKey) return TRUE; /* make sure our map is in charge */ if (map->control->activeInstance && map == map->control->activeInstance->map) { if (look->neighboursInfoValid && keySetFind(look->neighbours, key, 0)) return TRUE; if (key == look->selectKey) return TRUE; } return FALSE; } int gMapOverlap(MAPCONTROL map, KEY key, float z1, float z2) { GeneticMap look = (GeneticMap)map->look; COLCONTROL control = map->control; int flags = 0; float x1 = look->x1; float x2 = look->x2; double dd = -12.60 ; float ff = -12.60, ee ; /* ee is an estimate of rounding errors when computing with float */ ee = ff - dd ; if (ee < 0) ee = -ee ; ee *= 4 ; /* make sure our map is in charge */ if (control->activeInstance && map == control->activeInstance->map) { /* x1-x2 are the extremities of the selected seg who contains... */ /* z1-z2 segs */ if (look->friendsInfoValid && keySetFind(look->positive, key, 0)) { if (look->coOrdsInvalid) flags |= FLAG_RELATED; else if (x1 > z2 + ee || z1 - ee > x2) flags |= FLAG_STRESSED; else if ((x1 >= z1 - ee && x2 <= z2 + ee) || (z1 - ee >= x1 && z2 + ee <= x2)) flags |= FLAG_RELATED ; else /* staggered overlap */ flags |= FLAG_STRESSED ; } if (look->friendsInfoValid && keySetFind(look->negative, key, 0)) { if (look->coOrdsInvalid) flags |= FLAG_ANTI_RELATED; else if (x1 >= z2 + ee || z1 - ee >= x2) flags |= FLAG_ANTI_RELATED ; else if ((x1 > z1 - ee && x2 < z2 + ee) || (z1 - ee > x1 && z2 + ee < x2)) flags |= FLAG_ANTI_STRESSED ; else /* staggered overlap */ flags |= FLAG_ANTI_STRESSED ; } } return flags; } /* NB this is used by the markers column also */ int gMapOrder (void *a, void *b) /* for arraySort() call */ { GMAPSEG *seg1 = (GMAPSEG*)a, *seg2 = (GMAPSEG*)b ; float diff, x1, x2; if (seg1->flag & FLAG_ANY_INTERVAL) x1 = seg1->x - seg1->dx; /* sort on start posn of intervals */ else x1 = seg1->x; if (seg2->flag & FLAG_ANY_INTERVAL) x2 = seg2->x - seg2->dx; else x2 = seg2->x; diff = x1 - x2 ; if (diff > 0) return 1 ; else if (diff < 0) return -1 ; else return seg2->key - seg1->key ; /* never give a random result please */ } /* Convert Routine for locus, interval, and Chrom_band */ void *gMapConvert (MAPCONTROL map, void *params) { #define NPOS 1000 GeneticMap look = (GeneticMap)map->look; KEY chrom, clone, locus, cache; OBJ Locus; Array segs, loci; GMAPSEG *seg; float x[NPOS], dx[NPOS]; int i, j, flags, count; /* are they cached */ if ((cache = gMapCacheKey(map->key, "main")) && (segs = arrayGet(cache, GMAPSEG, segFormat))) { look->segs = segs; return segs; } segs = arrayCreate (128,GMAPSEG) ; /* This array is explicitly destroyed in gMapFinalise */ loci = gMapMapObjects(map->key) ; for (i = 0; ikey, chrom, 20, x, dx, &Locus, &count); if (!flags) continue; if (bsFindTag (Locus, _Well_ordered)) flags |= FLAG_WELL_ORDERED ; if (bsFindTag (Locus, _Centromere)) flags |= FLAG_CENTROMERE ; if (!bsFindTag (Locus, _Compound)) { if (bsFindTag (Locus, _Duplication)) flags |= FLAG_DUPLICATION ; if (bsFindTag (Locus, _Deletion) || bsFindTag (Locus, str2tag("Deficiency"))) flags |= FLAG_DEFICIENCY ; } if (bsGetKey (Locus,_Positive_clone,&clone)) flags |= FLAG_CLONED ; for (j=0; jkey = locus; seg->x = x[j] ; seg->dx = dx[j] ; seg->flag = flags; } bsDestroy (Locus) ; } arrayDestroy(loci); arraySort (segs, gMapOrder) ; gMapCacheStore(map->key, "main", segs, segFormat); look->segs = segs ; /* Make this display list commonly available. */ return (void *)segs ; } void gMapSaveDetails (Array segs, KEY map) { OBJ obj ; int i ; GMAPSEG *seg ; float x ; int nLocus = 0, nInterval = 0 ; /* destroy a cached set of segs */ gMapCacheKill(map); for (i = 0 ; i < arrayMax(segs) ; ++i) { seg = arrp(segs,i,GMAPSEG) ; if (seg->flag & FLAG_MOVED) { if (seg->flag & FLAG_ANY_LOCUS) { if ((obj = bsUpdate (seg->key))) { bsAddKey (obj, _Map, map) ; bsPushObj (obj) ; bsAddData (obj, _Position, _Float, &seg->x) ; bsPushObj (obj) ; bsAddData (obj, _Error, _Float, &seg->dx) ; bsSave (obj) ; ++nLocus ; } } else if (seg->flag & FLAG_ANY_INTERVAL) { if ((obj = bsUpdate (seg->key))) { bsAddKey (obj, _Map, map) ; bsPushObj (obj) ; x = seg->x - seg->dx; bsAddData (obj, _Left, _Float, &x) ; x = seg->x + seg->dx ; bsAddData (obj, _Right, _Float, &x) ; bsSave (obj) ; ++nInterval ; } } else messout ("%s has moved, but I don't know how to save it", name(seg->key)) ; } seg->flag &= ~FLAG_MOVED ; } messout ("Saved %d loci, %d intervals", nLocus, nInterval) ; } /************* end of file ****************/