/* * DBlabel2.c -- * * Label searching primitives. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/database/DBlabel2.c,v 1.3 2010/09/12 20:32:31 tim Exp $"; #endif /* not lint */ #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "database/databaseInt.h" #include "utils/malloc.h" /* * The following structure is used to pass data between DBNearestLabel * and its search function. */ struct nldata { int nld_distance; /* Square of distance to nearest * label seen so far. */ Point *nld_point; /* Reference: find nearest label to this */ Rect *nld_labelArea; /* Fill in with area of nearest label */ char *nld_name; /* Fill in with name of nearest label */ bool nld_gotLabel; /* TRUE means some label was found */ }; /* * The following structure is used to pass the arguments of * DBSearchLabel down to the filter function. */ #define MAXLABPATHSIZE 256 typedef struct { char *labSrPattern; /* Pattern for matching. */ int (*labSrFunc)(); /* Function to apply to each label found */ ClientData labSrArg; /* Client data of caller */ } labSrStruct; /* Forward declarations */ extern void DBTreeFindUse(); bool dbParseArray(); /* * ---------------------------------------------------------------------------- * * DBSearchLabel -- * * Search for all occurrences of a point label matching the pattern in the * region rect in the indicated cell and all of its children. On each label * matching the pattern found in the area, the supplied procedure is invoked. * * The supplied procedure should be of the form * int * func(scx, label, tpath, cdarg) * SearchContext *scx; * Label *label; * TerminalPath *tpath; * ClientData cdarg; * { * } * * In the above, scx is a search context specifying the cell use whose * def was found to contain the label, and label is a pointer to the * Label structure itself. The transform specified in scx is from * coordinates of the def of the cell containing the label to "root" * coordinates. Func should normally return 0. If it returns 1 then * the search is aborted. * * Results: * If the search terminates normally, 0 is returned. 1 is * returned if the search was aborted. * * Side effects: * Applies the supplied procedure to each tile containing a label * matching the pattern. * * WARNING: because of the way regex(3) works, it is possible to be * searching for at most one pattern at a time. * * ---------------------------------------------------------------------------- */ int DBSearchLabel(scx, mask, xMask, pattern, func, cdarg) SearchContext *scx; /* Search context: specifies CellUse, * transform to "root" coordinates, and * an area to search. */ TileTypeBitMask *mask; /* Only search for labels on these layers */ int xMask; /* Expansion state mask for searching. Cell * uses are only considered to be expanded * when their expand masks have all the bits * of xMask set. */ char *pattern; /* Pattern for which to search */ int (*func)(); /* Function to apply to each match */ ClientData cdarg; /* Argument to pass to function */ { TerminalPath tpath; int dbSrLabelFunc(); labSrStruct labsr; char labSrStr[MAXLABPATHSIZE]; /* String buffer in which the full pathname * of each label is assembled for handing * to the filter function. */ labsr.labSrPattern = pattern; labsr.labSrFunc = func; labsr.labSrArg = cdarg; tpath.tp_first = tpath.tp_next = labSrStr; tpath.tp_last = &(labSrStr[sizeof(labSrStr) - 2]); if (DBTreeSrLabels(scx, mask, xMask, &tpath, TF_LABEL_ATTACH, dbSrLabelFunc, (ClientData) &labsr)) return 1; else return 0; } /* * ---------------------------------------------------------------------------- * * dbSrLabelFunc -- * * Filter procedure applied to all labels by DBTreeSrLabels(). For * each label that matches the pattern set by DBSearchLabel(), the * filter function (*cdarg->lsa_func)() is applied. * * Results: * Always return 0 to keep the search going. * * Side effects: * Applies the supplied procedure to each label matching the pattern. * * ---------------------------------------------------------------------------- */ int dbSrLabelFunc(scx, label, tpath, labsr) SearchContext *scx; /* Contains pointer to use in which label * occurred, and transform back to root * coordinates. */ Label *label; /* Label itself */ TerminalPath *tpath; /* Full pathname of the terminal */ labSrStruct *labsr; /* Information passed to this routine */ { if (Match(labsr->labSrPattern, label->lab_text)) if ((*labsr->labSrFunc)(scx, label, tpath, labsr->labSrArg)) return 1; return 0; } /* * ---------------------------------------------------------------------------- * * DBSrLabelLoc -- * * This procedure finds the locations of all labels with a given * hierarchical name. For each label found, a client-supplied * search function is called. The search function has the form: * * int * func(rect, name, label, cdarg) * Rect *rect; * char *name; * Label *label; * ClientData cdarg; * * Rect is the location of the label, in the coordinates of rootUse->cu_def, * name is the label's hierarchical name (just the parameter passed to us), * label is a pointer to the label, and cdarg is the client data passed in * to us by the client. Note that there can be more than one label with the * same name. Func should normally return 0. If it returns 1, then the * search is aborted. * * Results: * The return value is 0, unless func returned a non-zero value, * in which case the return value is 1. * * Side effects: * Whatever the search function does. * * ---------------------------------------------------------------------------- */ int DBSrLabelLoc(rootUse, name, func, cdarg) CellUse *rootUse; /* Cell in which to search. */ char *name; /* A hierarchical label name consisting of zero or more * use-ids followed by a label name (fields separated * with slashes). */ int (*func)(); /* Applied to each instance of the label name */ ClientData cdarg; /* Data to pass through to (*func)() */ { CellDef *def; SearchContext scx; char *cp; Label *lab; char csave; Rect r; if ((cp = strrchr(name, '/'))) { csave = *cp; *cp = '\0'; DBTreeFindUse(name, rootUse, &scx); *cp = csave; if (scx.scx_use == NULL) return 0; cp++; } else { scx.scx_use = rootUse; scx.scx_trans = GeoIdentityTransform; cp = name; } def = scx.scx_use->cu_def; for (lab = def->cd_labels; lab; lab = lab->lab_next) if (lab->lab_text[0] == *cp && strcmp(lab->lab_text, cp) == 0) { GeoTransRect(&scx.scx_trans, &lab->lab_rect, &r); if ((*func)(&r, name, lab, cdarg)) return 1; } return 0; } /* * ---------------------------------------------------------------------------- * * DBTreeFindUse -- * * This procedure finds the cell use with the given hierarchical name. * * Results: * None. * * Side effects: * Sets scx->scx_use to the cell use found, with scx->scx_trans * and scx->scx_x, scx->scx_y also valid. If the cell was not * found, leaves scx->scx_use set to NULL. * * ---------------------------------------------------------------------------- */ void DBTreeFindUse(name, use, scx) char *name; CellUse *use; SearchContext *scx; { char *cp; HashEntry *he; CellDef *def; char csave; def = use->cu_def; scx->scx_use = (CellUse *) NULL; scx->scx_trans = GeoIdentityTransform; scx->scx_x = scx->scx_y = 0; while (*name) { /* * Make sure that the cell whose children are being searched * is read in from disk. */ if ((def->cd_flags & CDAVAILABLE) == 0) (void) DBCellRead(def, TRUE, TRUE, NULL); cp = name; he = HashLookOnly(&def->cd_idHash, name); if (he == NULL || HashGetValue(he) == NULL) { /* * Pull off the next component of path up to but not including * any array subscripts. * NOTE: This should check the array bounds and only remove * array components that are expected, not array components * embedded in the name. */ for (; *cp && *cp != '[' && *cp != '/'; cp++) /* Nothing */; csave = *cp; *cp = '\0'; he = HashLookOnly(&def->cd_idHash, name); *cp = csave; if (he == NULL || HashGetValue(he) == NULL) return; } use = (CellUse *) HashGetValue(he); def = use->cu_def; /* * Pull off array subscripts and build next stage in transform. * Return NULL if the number of subscripts specified doesn't * match the number that are implied by the array, if use is * an array. */ if (!dbParseArray(cp, use, scx)) { /* Allow non-indexed match of array */ if (strcmp(name, use->cu_id)) return; /* Check for both 1- and 2-dimensional arrays */ if (!dbParseArray("[0][0]", use, scx)) if (!dbParseArray("[0]", use, scx)) return; break; } while (*cp && *cp++ != '/') /* Nothing */; name = cp; } /* Ensure that the leaf cell is read in */ def = use->cu_def; if ((def->cd_flags & CDAVAILABLE) == 0) DBCellRead(def, TRUE, TRUE, NULL); scx->scx_use = use; } /* * ---------------------------------------------------------------------------- * * dbParseArray -- * * Pull off the array subscripts starting at 'cp' (there may be none), * checking to ensure that there are the correct number for 'use' and that * they are in range. Store these in scx->scx_x and scx->scx_y, and use them * to update scx->scx_trans to be use->cu_trans (as adjusted for the indicated * array element) followed by the old value of scx->scx_trans. * * Results: * Returns TRUE on success, FALSE on error. * * Side effects: * See above. * * ---------------------------------------------------------------------------- */ bool dbParseArray(cp, use, scx) char *cp; CellUse *use; SearchContext *scx; { int xdelta, ydelta, i1, i2, indexCount; Transform trans, trans2; /* * The transform stuff is a little tricky because if there * was only one index given we don't know whether it's the * x- or y-index. Make sure the number of indices specified * matches the number of dimensions in an array, and that * the indices are in range. */ indexCount = 0; if (*cp == '[') { if (sscanf(cp, "[%d][%d]", &i1, &i2) == 2) { indexCount = 2; while (*cp++ != ']') /* Nothing */; while (*cp++ != ']') /* Nothing */; } else if (sscanf(cp, "[%d,%d]", &i1, &i2) == 2) { indexCount = 2; while (*cp++ != ']') /* Nothing */; } else if (sscanf(cp, "[%d]", &i1) == 1) { indexCount = 1; while (*cp++ != ']') /* Nothing */; } if (indexCount && *cp != '\0' && *cp != '/') return FALSE; } switch (indexCount) { case 0: if (use->cu_xlo != use->cu_xhi || use->cu_ylo != use->cu_yhi) return FALSE; scx->scx_x = use->cu_xlo; scx->scx_y = use->cu_ylo; break; case 1: if (use->cu_xlo == use->cu_xhi) { scx->scx_x = use->cu_xlo; scx->scx_y = i1; } else if (use->cu_ylo == use->cu_yhi) { scx->scx_x = i1; scx->scx_y = use->cu_ylo; } else return FALSE; break; case 2: if (use->cu_xlo == use->cu_xhi || use->cu_ylo == use->cu_yhi) return FALSE; scx->scx_y = i1; scx->scx_x = i2; break; } if (use->cu_xhi > use->cu_xlo) { if (scx->scx_x < use->cu_xlo || scx->scx_x > use->cu_xhi) return FALSE; xdelta = use->cu_xsep * (scx->scx_x - use->cu_xlo); } else { if (scx->scx_x > use->cu_xlo || scx->scx_x < use->cu_xhi) return FALSE; xdelta = use->cu_xsep * (use->cu_xlo - scx->scx_x); } if (use->cu_yhi > use->cu_ylo) { if (scx->scx_y < use->cu_ylo || scx->scx_y > use->cu_yhi) return FALSE; ydelta = use->cu_ysep * (scx->scx_y - use->cu_ylo); } else { if (scx->scx_y > use->cu_ylo || scx->scx_y < use->cu_yhi) return FALSE; ydelta = use->cu_ysep * (use->cu_ylo - scx->scx_y); } GeoTransTranslate(xdelta, ydelta, &use->cu_transform, &trans); GeoTransTrans(&trans, &scx->scx_trans, &trans2); scx->scx_trans = trans2; return TRUE; } /* * ---------------------------------------------------------------------------- * * DBNearestLabel -- * * This procedure finds the nearest label to a given point * and returns its hierarchical name and location. * * Results: * Area is searched in cellUse to find the nearest label * to point. TRUE is returned if any label was found. * If there is no label in the given area, FALSE is * returned. * * Side effects: * The parameter labelArea is filled in with the location of * the label, if one was found. LabelName is filled in with * the hierarchical name of the label. * * ---------------------------------------------------------------------------- */ bool DBNearestLabel(cellUse, area, point, xMask, labelArea, labelName, length) CellUse *cellUse; /* Start search at this cell. */ Rect *area; /* Search this area of cellUse. */ Point *point; /* Find nearest label to this point. */ int xMask; /* Recursively search subcells as long * as their expand masks, when anded with * xMask, are equal to xMask. 0 means search * all the way down through the hierarchy. */ Rect *labelArea; /* To be filled in with area of closest * label. NULL means ignore. */ char *labelName; /* Fill this in with name of label, unless * NULL. */ int length; /* This gives the maximum number of chars * that may be used in labelName, including * the NULL character to terminate. */ { TerminalPath tPath, *tp; SearchContext scx; char *name; struct nldata funcData; extern int dbNearestLabelFunc(); /* Allocate space to generate a label name, and set up information * for the DBTreeSrLabels call. */ if (labelName == NULL) tp = NULL, name = NULL; else { name = (char *) mallocMagic((unsigned) (length)); tPath.tp_first = name; tPath.tp_next = name; tPath.tp_last = name + length - 1; tp = &tPath; } scx.scx_use = cellUse; scx.scx_area = *area; scx.scx_trans = GeoIdentityTransform; funcData.nld_point = point; funcData.nld_labelArea = labelArea; funcData.nld_name = labelName; funcData.nld_gotLabel = FALSE; (void) DBTreeSrLabels(&scx, &DBAllTypeBits, xMask, tp, TF_LABEL_ATTACH, dbNearestLabelFunc, (ClientData) &funcData); if (name) freeMagic(name); if (!funcData.nld_gotLabel) return FALSE; else return TRUE; } /* * ---------------------------------------------------------------------------- * * dbNearestLabelFunc -- * * This function is called by DBTreeSrLabels for each label * found as part of DBNearestLabel. * * Results: * Always returns 0 to continue the search. * * Side effects: * If this is the closest label seen so far, update the information * passed via funcData. * * ---------------------------------------------------------------------------- */ int dbNearestLabelFunc(scx, label, tpath, funcData) SearchContext *scx; /* Describes state of search. */ Label *label; /* Label found. */ TerminalPath *tpath; /* Name of label. */ struct nldata *funcData; /* Parameters to DBNearestLabel (passed as * ClientData). */ { int x, y, distance, left, used; Rect area; char *src, *dst; GeoTransRect(&scx->scx_trans, &label->lab_rect, &area); x = (area.r_xtop + area.r_xbot)/2 - funcData->nld_point->p_x; y = (area.r_ytop + area.r_ybot)/2 - funcData->nld_point->p_y; distance = x*x + y*y; if ((funcData->nld_gotLabel) && (distance > funcData->nld_distance)) return 0; funcData->nld_distance = distance; funcData->nld_gotLabel = TRUE; if (funcData->nld_labelArea != NULL) *(funcData->nld_labelArea) = area; if (funcData->nld_name != NULL) { left = tpath->tp_last - tpath->tp_next; used = tpath->tp_next - tpath->tp_first; (void) strncpy(funcData->nld_name, tpath->tp_first, used); dst = funcData->nld_name + used; src = label->lab_text; for ( ; left > 0; left -= 1) { if (*src == 0) break; *dst++ = *src++; } *dst = 0; } return 0; }