/*! * \return Distance object which shares the given foreground object's * domain and has integer distance values, null on error. * \ingroup WlzMorphologyOps * \brief Computes the distance of every pixel/voxel in the foreground * object from the reference object. * * A distance transform maps all position within a forground * domain to their distances from a reference domain. * The distance transforms implemented within this function * use efficient morphological primitives. * * Given two domains, * \f$\Omega_r\f$ the reference domain and \f$\Omega_f\f$ * the domain specifying the region of interest, * a domain with a thin shell \f$\Omega_i\f$ * is iteratively expanded from it's initial domain * corresponding to the reference domain \f$\Omega_r\f$. * At each iteration * \f$\Omega_i\f$ is dilated and clipped * by it's intersection with \f$\Omega_f\f$ until \f$\Omega_i\f$ * becomes the null domain \f$\emptyset\f$. * At each iteration the current distance is recorded in a value * table which * covers the domain \f$\Omega_f\f$. * * An octagonal distance scheme may be used in which * the distance metric is alternated between 4 and 8 * connected for 2D and 6 and 26 connectivities in 3D. * See: G. Borgefors. "Distance Transformations in Arbitrary * Dimensions" CVGIP 27:321-345, 1984. * * An approximate Euclidean distance transform may be computed * by: Scaling the given foreground and reference objects using * the given approximation scale parameter, dilating the * reference domain using a sphere with a radius having the same * value as the scale parameter and then finaly sampling the * scaled distances. * \param forObj Foreground object. * \param refObj Reference object. * \param dFn Distance function which must be * appropriate to the dimension of * the foreground and reference objects. * \param dParam Parameter required for distance * function. Currently only * WLZ_APX_EUCLIDEAN_DISTANCE requires a * parameter. In this case the parameter * is the approximation scale. * \param dstErr Destination error pointer, may be NULL. */ WlzObject *WlzDistanceTransform(WlzObject *forObj, WlzObject *refObj, WlzDistanceType dFn, double dParam, WlzErrorNum *dstErr) { int idP, lastP, dim, notDone = 1; double scale; WlzObject *tmpObj, *sObj = NULL, *sForObj = NULL, *sRefObj = NULL, *dilObj = NULL, *dstObj = NULL, *difObj = NULL, *curItrObj = NULL; WlzObject *bothObj[2]; WlzDomain *difDoms; WlzPixelV dstV, bgdV; WlzValues *difVals; WlzAffineTransform *tr = NULL; WlzConnectType con; WlzObjectType dstGType; WlzErrorNum errNum = WLZ_ERR_NONE; WlzValues difVal, dstVal, nullVal; /* By defining WLZ_DIST_TRANSFORM_ENV these normalization parameters * are read from the environment. This is useful for optimization. */ #ifndef WLZ_DIST_TRANSFORM_ENV const #endif /* ! WLZ_DIST_TRANSFORM_ENV */ /* These normalizarion factors have been choosen to minimize the sum of * squares of the deviation of the distance values from Euclidean values * over a radius 100 circle or sphere, where the distances are computed * from the circumference of the sphere towards it's centre. The values * were established by experiment. */ double nrmDist4 = 0.97, nrmDist6 = 0.91, nrmDist8 = 1.36, nrmDist18 = 1.34, nrmDist26 = 1.60; #ifdef WLZ_DIST_TRANSFORM_ENV double val; char *envStr; if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST4")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist4 = val; } if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST6")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist6 = val; } if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST8")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist8 = val; } if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST18")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist18 = val; } if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST26")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist26 = val; } #endif /* WLZ_DIST_TRANSFORM_ENV */ scale = dParam; nullVal.core = NULL; /* Check parameters. */ if((forObj == NULL) || (refObj == NULL)) { errNum = WLZ_ERR_OBJECT_NULL; } else if(((forObj->type != WLZ_2D_DOMAINOBJ) && (forObj->type != WLZ_3D_DOMAINOBJ)) || ((refObj->type != WLZ_POINTS) && (refObj->type != forObj->type))) { errNum = WLZ_ERR_OBJECT_TYPE; } else if((forObj->domain.core == NULL) || (refObj->domain.core == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } if(errNum == WLZ_ERR_NONE) { bgdV.type = WLZ_GREY_INT; bgdV.v.inv = 0; dstV.type = WLZ_GREY_DOUBLE; dstV.v.dbv = 0.0; switch(forObj->type) { case WLZ_2D_DOMAINOBJ: switch(dFn) { case WLZ_4_DISTANCE: /* FALLTHROUGH */ case WLZ_8_DISTANCE: /* FALLTHROUGH */ case WLZ_OCTAGONAL_DISTANCE: /* FALLTHROUGH */ case WLZ_APX_EUCLIDEAN_DISTANCE: dim = 2; break; default: errNum = WLZ_ERR_PARAM_DATA; break; } break; case WLZ_3D_DOMAINOBJ: switch(dFn) { case WLZ_6_DISTANCE: /* FALLTHROUGH */ case WLZ_18_DISTANCE: /* FALLTHROUGH */ case WLZ_26_DISTANCE: /* FALLTHROUGH */ case WLZ_OCTAGONAL_DISTANCE: /* FALLTHROUGH */ case WLZ_APX_EUCLIDEAN_DISTANCE: dim = 3; break; default: errNum = WLZ_ERR_PARAM_DATA; break; } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if(errNum == WLZ_ERR_NONE) { switch(dFn) { case WLZ_4_DISTANCE: con = WLZ_4_CONNECTED; break; case WLZ_6_DISTANCE: con = WLZ_6_CONNECTED; break; case WLZ_8_DISTANCE: con = WLZ_8_CONNECTED; break; case WLZ_18_DISTANCE: con = WLZ_18_CONNECTED; break; case WLZ_26_DISTANCE: con = WLZ_26_CONNECTED; break; case WLZ_OCTAGONAL_DISTANCE: con = (dim == 2)? WLZ_8_CONNECTED: WLZ_26_CONNECTED; break; case WLZ_APX_EUCLIDEAN_DISTANCE: con = (dim == 2)? WLZ_8_CONNECTED: WLZ_26_CONNECTED; if(scale < 1.0) { errNum = WLZ_ERR_PARAM_DATA; } break; case WLZ_EUCLIDEAN_DISTANCE: errNum = WLZ_ERR_UNIMPLEMENTED; break; default: errNum = WLZ_ERR_PARAM_DATA; break; } } /* Create scaled domains and a sphere domain for structual erosion if the * distance function is approximate Euclidean. */ if(errNum == WLZ_ERR_NONE) { if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE) { tr = (dim == 2)? WlzAffineTransformFromScale(WLZ_TRANSFORM_2D_AFFINE, scale, scale, 0.0, &errNum): WlzAffineTransformFromScale(WLZ_TRANSFORM_3D_AFFINE, scale, scale, scale, &errNum); if(errNum == WLZ_ERR_NONE) { tmpObj = WlzMakeMain(forObj->type, forObj->domain, nullVal, NULL, NULL, &errNum); if(tmpObj) { sForObj = WlzAssignObject( WlzAffineTransformObj(tmpObj, tr, WLZ_INTERPOLATION_NEAREST, &errNum), NULL); (void )WlzFreeObj(tmpObj); } } if(errNum == WLZ_ERR_NONE) { if(refObj->type == WLZ_POINTS) { sRefObj = WlzPointsToDomObj(refObj->domain.pts, scale, &errNum); } else /* type == WLZ_2D_DOMAINOBJ || type == WLZ_3D_DOMAINOBJ */ { tmpObj = WlzMakeMain(refObj->type, refObj->domain, nullVal, NULL, NULL, &errNum); if(errNum == WLZ_ERR_NONE) { sRefObj = WlzAssignObject( WlzAffineTransformObj(tmpObj, tr, WLZ_INTERPOLATION_NEAREST, &errNum), NULL); } } (void )WlzFreeObj(tmpObj); } if(errNum == WLZ_ERR_NONE) { sObj = WlzAssignObject( WlzMakeSphereObject(forObj->type, scale, 0.0, 0.0, 0.0, &errNum), NULL); } (void )WlzFreeAffineTransform(tr); } else { sForObj = WlzAssignObject( WlzMakeMain(forObj->type, forObj->domain, nullVal, NULL, NULL, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { if(refObj->type == WLZ_POINTS) { sRefObj = WlzPointsToDomObj(refObj->domain.pts, 1.0, &errNum); } else { sRefObj = WlzAssignObject( WlzMakeMain(refObj->type, refObj->domain, nullVal, NULL, NULL, &errNum), NULL); } } } } /* Create new values for the computed distances. */ if(errNum == WLZ_ERR_NONE) { dstGType = WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_INT, NULL); if(dim == 2) { dstVal.v = WlzNewValueTb(sForObj, dstGType, bgdV, &errNum); } else { dstVal.vox = WlzNewValuesVox(sForObj, dstGType, bgdV, &errNum); } } /* Create a distance object using the foreground object's domain and * the new distance values. */ if(errNum == WLZ_ERR_NONE) { dstObj = WlzMakeMain(sForObj->type, sForObj->domain, dstVal, NULL, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { bothObj[0] = sForObj; errNum = WlzGreySetValue(dstObj, dstV); } /* Dilate the reference object while setting the distances in each * dilated shell. */ while((errNum == WLZ_ERR_NONE) && notDone) { if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE) { dstV.v.dbv += 1.0; } else { switch(con) { case WLZ_4_CONNECTED: dstV.v.dbv += nrmDist4; break; case WLZ_6_CONNECTED: dstV.v.dbv += nrmDist6; break; case WLZ_8_CONNECTED: dstV.v.dbv += nrmDist8; break; case WLZ_18_CONNECTED: dstV.v.dbv += nrmDist18; break; case WLZ_26_CONNECTED: dstV.v.dbv += nrmDist26; break; default: errNum = WLZ_ERR_CONNECTIVITY_TYPE; break; } } if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE) { dilObj = WlzStructDilation(sRefObj, sObj, &errNum); } else { dilObj = WlzDilation(sRefObj, con, &errNum); } if(errNum == WLZ_ERR_NONE) { switch(sForObj->type) { case WLZ_2D_DOMAINOBJ: curItrObj = WlzAssignObject( WlzIntersect2(dilObj, sForObj, &errNum), NULL); break; case WLZ_3D_DOMAINOBJ: bothObj[1] = dilObj; curItrObj = WlzAssignObject( WlzIntersectN(2, bothObj, 1, &errNum), NULL); break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } (void)WlzFreeObj(dilObj); /* Create difference object for the expanding shell. */ if(errNum == WLZ_ERR_NONE) { difObj = WlzDiffDomain(curItrObj, sRefObj, &errNum); } if((difObj == NULL) || WlzIsEmpty(difObj, &errNum)) { notDone = 0; } else { /* Assign the distance object's values to the difference object * and set all it's values to the current distance. */ if(errNum == WLZ_ERR_NONE) { switch(sForObj->type) { case WLZ_2D_DOMAINOBJ: difObj->values = WlzAssignValues(dstObj->values, NULL); errNum = WlzGreySetValue(difObj, dstV); break; case WLZ_3D_DOMAINOBJ: /* 3D is more complex than 2D: Need to create a temporary * voxel valuetable and assign the individual 2D values. */ difVal.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, difObj->domain.p->plane1, difObj->domain.p->lastpl, bgdV, NULL, &errNum); if(errNum == WLZ_ERR_NONE) { difObj->values = WlzAssignValues(difVal, NULL); difDoms = difObj->domain.p->domains; difVals = difObj->values.vox->values; idP = difObj->domain.p->plane1; lastP = difObj->domain.p->lastpl; while(idP <= lastP) { if((*difDoms).core) { dstVal = dstObj->values.vox->values[idP - dstObj->domain.p->plane1]; *difVals = WlzAssignValues(dstVal, NULL); } ++idP; ++difDoms; ++difVals; } if(difObj->domain.p->lastpl > difObj->domain.p->plane1) { errNum = WlzGreySetValue(difObj, dstV); } } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } (void )WlzFreeObj(sRefObj); sRefObj = WlzAssignObject(curItrObj, NULL); (void )WlzFreeObj(curItrObj); } (void )WlzFreeObj(difObj); difObj = NULL; if(dFn == WLZ_OCTAGONAL_DISTANCE) { /* Alternate connectivities for octagonal distance. */ if(dim == 2) { con = (con == WLZ_4_CONNECTED)? WLZ_8_CONNECTED: WLZ_4_CONNECTED; } else /* dim == 3 */ { con = (con == WLZ_6_CONNECTED)? WLZ_26_CONNECTED: WLZ_6_CONNECTED; } } } (void )WlzFreeObj(sObj); (void )WlzFreeObj(sForObj); (void )WlzFreeObj(sRefObj); (void )WlzFreeObj(curItrObj); if((errNum == WLZ_ERR_NONE) && (dFn == WLZ_APX_EUCLIDEAN_DISTANCE)) { tmpObj = WlzDistSample(dstObj, dim, scale, &errNum); (void )WlzFreeObj(dstObj); dstObj = tmpObj; } if(errNum != WLZ_ERR_NONE) { (void )WlzFreeObj(dstObj); dstObj = NULL; } if(dstErr) { *dstErr = errNum; } return(dstObj); }
/*! * \return Woolz error code. * \ingroup WlzBinaryOps * \brief Computes a metric which quantifies the extent to which * the domain of one of the given objects is offset from * the domain of the second. This is a symetric metric, ie * \f$OST(\Omega_0, \Omega_1) \equiv OST(\Omega_0, \Omega_1)\f$. * A domain is computed which is equidistant from the domains * of the two given objects, is within maxDist of each object's * domain and is within the convex hull of the union of the * domains of the two given objects. Within this domain the * 1st, 2nd and 3rd quantiles of the distance * (\f$q_0\f$, \f$q_1\f$ and \f$q_2\f$) are found. * The object's domains are classified as offset if * \f[ frac{q_1}{q_1 + (q_1 - q_0) + (q_2 - q_1)} \geq 0.5 \f] * ie * \f[ frac{q_1}{q_1 + q_2 - q_0} \geq 0.5 \f] * Small equi-distant domains with a volume less than half * the maximum distance do not classify the relationship as * an overlap. * \param o Array with the two given spatial * domain objects, must not be NULL * and nor must the objects. * \param t Array of temporary objects as in * WlzRCCTOIdx. * \param maxDist Maximum distance for offset. This * is used to compute a distance object, * large distances will significantly * increase the processing time. * \param dQ0 Destination pointer for 1st quantile * offset distance, must not be NULL. * \param dQ1 Destination pointer for 2nd quantile * (ie median) offset distance, * must not be NULL. * \param dQ2 Destination pointer for 3rd quantile * offset distance, must not be NULL. */ static WlzErrorNum WlzRCCOffset( WlzObject **o, WlzObject **t, int maxDist, int *dQ0, int *dQ1, int *dQ2) { int empty = 0; int q[3] = {0}; int *dHist = NULL; WlzObject *eObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; if(((o[0]->type == WLZ_2D_DOMAINOBJ) && (o[1]->type == WLZ_2D_DOMAINOBJ)) || ((o[0]->type == WLZ_3D_DOMAINOBJ) && (o[1]->type == WLZ_3D_DOMAINOBJ))) { if((o[0]->domain.core == NULL) || (o[1]->domain.core == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } } else { errNum = WLZ_ERR_OBJECT_TYPE; } /* Compute distance transforms of the two given objects out to a given * maximum distance and then using these distances the equi-distant * domain between these two objects. The values of the eqi-distant object * are those of the distance between the objects.*/ if(errNum == WLZ_ERR_NONE) { int i; WlzObject *sObj = NULL, *cObj = NULL; WlzObject *dObj[2]; dObj[0] = dObj[1] = NULL; /* Create structuring element with which to dilate the given object * domains(by maxDist). */ sObj = WlzAssignObject( WlzMakeSphereObject(o[0]->type, maxDist, 0, 0, 0, &errNum), NULL); /* Create domain or convex hull of the union of the two given object * domains. */ if(errNum == WLZ_ERR_NONE) { WlzObject *uObj = NULL, *xObj = NULL; errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1U); if(errNum == WLZ_ERR_NONE) { uObj = WlzAssignObject(t[WLZ_RCCTOIDX_O0O1U], NULL); } if(errNum == WLZ_ERR_NONE) { xObj = WlzAssignObject( WlzObjToConvexHull(uObj, &errNum), NULL); } if(errNum == WLZ_ERR_NONE) { cObj = WlzAssignObject( WlzConvexHullToObj(xObj, o[0]->type, &errNum), NULL); } (void )WlzFreeObj(xObj); (void )WlzFreeObj(uObj); } /* Dilate the two given objects and find the ntersection of the * dilated domains with each other and the convex hull computed * above. Within his domain compute the distances. */ if(errNum == WLZ_ERR_NONE) { for(i = 0; i < 2; ++i) { WlzObject *tObj = NULL, *rObj = NULL; tObj = WlzAssignObject( WlzStructDilation(o[i], sObj, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { rObj = WlzAssignObject( WlzIntersect2(tObj, cObj, &errNum), NULL); } (void )WlzFreeObj(tObj); if(errNum == WLZ_ERR_NONE) { dObj[i] = WlzAssignObject( WlzDistanceTransform(rObj, o[!i], WLZ_OCTAGONAL_DISTANCE, 0.0, maxDist, &errNum), NULL); } (void )WlzFreeObj(rObj); if(errNum == WLZ_ERR_NONE) { WlzPixelV bgdV; bgdV.type = WLZ_GREY_INT; bgdV.v.inv = maxDist; errNum = WlzSetBackground(dObj[i], bgdV); } if(errNum != WLZ_ERR_NONE) { break; } } } /* Find the domain which is equi-distant from the two given objects, * within the xDist range and within the convex hull of the union of * the two given object's domains. */ (void )WlzFreeObj(sObj); sObj = NULL; if(errNum == WLZ_ERR_NONE) { WlzLong vol = 0; WlzObject *qObj = NULL, *tObj = NULL; qObj = WlzAssignObject( WlzImageArithmetic(dObj[0], dObj[1], WLZ_BO_EQ, 0, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { WlzPixelV thrV; thrV.type = WLZ_GREY_INT; thrV.v.inv = 1; tObj = WlzAssignObject( WlzThreshold(qObj, thrV, WLZ_THRESH_HIGH, &errNum), NULL); } /* Check that the eqi-distant domain is of a reasonable size ie has * a area or volume greater than half the maximum distance. */ if(errNum == WLZ_ERR_NONE) { vol = WlzVolume(tObj, &errNum); if((maxDist / 2) >= vol) { empty = 1; } } if((errNum == WLZ_ERR_NONE) && !empty) { WlzObject *mObj; WlzPixelV tmpV; tmpV.type = WLZ_GREY_INT; tmpV.v.inv = 0; mObj = WlzAssignObject( WlzGreyTemplate(dObj[0], tObj, tmpV, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { tmpV.v.inv = 1; eObj = WlzAssignObject( WlzThreshold(mObj, tmpV, WLZ_THRESH_HIGH, &errNum), NULL); } (void )WlzFreeObj(mObj); } (void )WlzFreeObj(tObj); (void )WlzFreeObj(qObj); if((errNum == WLZ_ERR_NONE) && !empty) { WlzLong vol; vol = WlzVolume(eObj, &errNum); if((maxDist / 2) >= vol) { empty = 1; } } } (void )WlzFreeObj(cObj); (void )WlzFreeObj(sObj); (void )WlzFreeObj(dObj[0]); (void )WlzFreeObj(dObj[1]); } /* Compute a quantised distance histogram in which equi-distant distances * are quantized to integer values. */ if((errNum == WLZ_ERR_NONE) && !empty) { if((dHist = (int *)AlcCalloc(maxDist + 1, sizeof(int))) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } if((errNum == WLZ_ERR_NONE) && !empty) { if(eObj->type == WLZ_2D_DOMAINOBJ) { errNum = WlzRCCCompDistHist2D(maxDist, dHist, eObj); } else { errNum = WlzRCCCompDistHist3D(maxDist, dHist, eObj); } #ifdef WLZ_RCC_DEBUG_OST { FILE *fP; fP = fopen("WLZ_RCC_DEBUG_OST.wlz", "w"); (void )WlzWriteObj(fP, eObj); (void )fclose(fP); } #endif } WlzFreeObj(eObj); if((errNum == WLZ_ERR_NONE) && !empty) { int i, n, s, nq; /* Compute the median, first and third quantile offset distances, * the ratio of median to the median plus inner inter-quantile range. */ n = 0; for(i = 0; i < maxDist; ++i) { n += dHist[i]; } i = 0; s = 0; nq = n / 4; while(s < nq) { s += dHist[i++]; } q[0] = i; nq = n / 2; while(s < nq) { s += dHist[i++]; } q[1] = i; nq = (3 * n) / 4; while(s < nq) { s += dHist[i++]; } q[2] = i; } AlcFree(dHist); *dQ0 = q[0]; *dQ1 = q[1]; *dQ2 = q[2]; return(errNum); }
void postProcMorphCb( Widget w, XtPointer client_data, XtPointer call_data) { MAPaintOperatorType opType=(MAPaintOperatorType) client_data; WlzObject *seObj, *newDomain=NULL, *tmpObj; WlzConnectType conn; WlzErrorNum errNum=WLZ_ERR_NONE; /* check signal object */ if( warpGlobals.sgnlObj == NULL ){ return; } /* build the structuring element */ seObj = NULL; conn = WLZ_4_CONNECTED; switch( warpGlobals.seType ){ case MAPAINT_8_CONN_SE: conn = WLZ_8_CONNECTED; break; case MAPAINT_4_CONN_SE: conn = WLZ_4_CONNECTED; break; case MAPAINT_CIRCLE_SE: if( warpGlobals.seElemRadius != 1 ){ seObj = WlzMakeCircleObject((double) warpGlobals.seElemRadius, 0.0, 0.0, &errNum); } break; case MAPAINT_SQUARE_SE: if( warpGlobals.seElemRadius != 1 ){ seObj = WlzMakeRectangleObject((double) warpGlobals.seElemRadius, (double) warpGlobals.seElemRadius, 0.0, 0.0, &errNum); } break; case MAPAINT_SPHERE_SE: case MAPAINT_CUBE_SE: default: return; } /* apply the required operation */ switch( opType ){ case MAPAINT_OP_ERODE: if( seObj ){ newDomain = WlzStructErosion(warpGlobals.sgnlObj, seObj, &errNum); } else { newDomain = WlzErosion(warpGlobals.sgnlObj, conn, &errNum); } break; case MAPAINT_OP_DILATE: if( seObj ){ newDomain = WlzStructDilation(warpGlobals.sgnlObj, seObj, &errNum); } else { newDomain = WlzDilation(warpGlobals.sgnlObj, conn, &errNum); } break; case MAPAINT_OP_OPEN: if( seObj ){ if((tmpObj = WlzStructErosion(warpGlobals.sgnlObj, seObj, &errNum))){ newDomain = WlzStructDilation(tmpObj, seObj, &errNum); } else { newDomain = NULL; } } else { if((tmpObj = WlzErosion(warpGlobals.sgnlObj, conn, &errNum))){ newDomain = WlzDilation(tmpObj, conn, &errNum); } else { newDomain = NULL; } } if( tmpObj ){ WlzFreeObj(tmpObj); } break; case MAPAINT_OP_CLOSE: if( seObj ){ if((tmpObj = WlzStructDilation(warpGlobals.sgnlObj, seObj, &errNum))){ newDomain = WlzStructErosion(tmpObj, seObj, &errNum); } else { newDomain = NULL; } } else { if((tmpObj = WlzDilation(warpGlobals.sgnlObj, conn, &errNum))){ newDomain = WlzErosion(tmpObj, conn, &errNum); } else { newDomain = NULL; } } if( tmpObj ){ WlzFreeObj(tmpObj); } break; default: break; } /* push existing onto undo stack note the domain is not incremented */ /* should have an independent undo and redo stack here */ if( newDomain ){ WlzObjListPush(sgnlPostProcUndoList, warpGlobals.sgnlObj); WlzObjListClear(sgnlPostProcRedoList); WlzFreeObj(warpGlobals.sgnlObj); warpGlobals.sgnlObj = WlzAssignObject(newDomain, &errNum); warpCanvasExposeCb(w, (XtPointer) &(warpGlobals.sgnl), NULL); warpDisplayDomain(&(warpGlobals.sgnl), warpGlobals.sgnlObj, 1); XFlush(XtDisplayOfObject(w)); } return; }