/*! * \return Woolz error code. * \ingroup WlzBinaryOps * \brief Make temporary objects as in WlzRCCTOIdx for * WlzRegConCalcRCC(). * \param o Array of objects, o[0] and o[1]. * \param t Array of temporary objects. * \param i Temporary object index. */ static WlzErrorNum WlzRCCMakeT( WlzObject **o, WlzObject **t, WlzRCCTOIdx i) { WlzErrorNum errNum = WLZ_ERR_NONE; if((i >= 0) && (i < WLZ_RCCTOIDX_CNT)) { if(t[i] == NULL) { switch(i) { case WLZ_RCCTOIDX_O0O1U: /* o_0 \cup \o_1 */ t[i] = WlzAssignObject( WlzUnion2(o[0], o[1], &errNum), NULL); break; case WLZ_RCCTOIDX_O0O1I: /* o_0 \cap \o_1 */ t[i] = WlzAssignObject( WlzIntersect2(o[0], o[1], &errNum), NULL); break; case WLZ_RCCTOIDX_O0D: /* o_0^+ */ t[i] = WlzAssignObject( WlzDilation(o[0], (o[0]->type == WLZ_2D_DOMAINOBJ)? WLZ_8_CONNECTED: WLZ_26_CONNECTED, &errNum), NULL); break; case WLZ_RCCTOIDX_O1D: /* o_1^+ */ t[i] = WlzAssignObject( WlzDilation(o[1], (o[1]->type == WLZ_2D_DOMAINOBJ)? WLZ_8_CONNECTED: WLZ_26_CONNECTED, &errNum), NULL); break; case WLZ_RCCTOIDX_O0F: /* o_0^{\bullet} */ t[i] = WlzAssignObject( WlzDomainFill(o[0], &errNum), NULL); break; case WLZ_RCCTOIDX_O1F: /* o_1^{\bullet} */ t[i] = WlzAssignObject( WlzDomainFill(o[1], &errNum), NULL); break; case WLZ_RCCTOIDX_O0DO1U: /* o_0^+ \cup o_1 */ errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0D); if(errNum == WLZ_ERR_NONE) { t[i] = WlzAssignObject( WlzUnion2(t[WLZ_RCCTOIDX_O0D], o[1], &errNum), NULL); } break; case WLZ_RCCTOIDX_O0O1DU: /* o_0 \cup o_1^+ */ errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O1D); if(errNum == WLZ_ERR_NONE) { t[i] = WlzAssignObject( WlzUnion2(o[0], t[WLZ_RCCTOIDX_O1D], &errNum), NULL); } break; case WLZ_RCCTOIDX_O0CO1I: /* o_0^{\circ} \cap o_1 */ case WLZ_RCCTOIDX_O0O1CI: /* FALLTHROUGH o_0 \cap o_1^{\circ} */ { int i0; WlzObject *c = NULL, *x = NULL; i0 = (i == WLZ_RCCTOIDX_O0CO1I)? 0: 1; c = WlzObjToConvexHull(o[i0], &errNum); if((errNum == WLZ_ERR_NONE) || (errNum == WLZ_ERR_DEGENERATE)) { x = WlzAssignObject( WlzConvexHullToObj(c, o[i0]->type, &errNum), NULL); } if(errNum == WLZ_ERR_NONE) { t[i] = WlzAssignObject( WlzIntersect2(o[!i0], x, &errNum), NULL); } (void )WlzFreeObj(c); (void )WlzFreeObj(x); } break; case WLZ_RCCTOIDX_O0FO1U: /* o_0^{\bullet} \cup o_1 */ errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0F); if(errNum == WLZ_ERR_NONE) { t[i] = WlzAssignObject( WlzUnion2(t[WLZ_RCCTOIDX_O0F], o[1], &errNum), NULL); } break; case WLZ_RCCTOIDX_O0O1FU: /* o_0 \cup o_1^{\bullet} */ errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O1F); if(errNum == WLZ_ERR_NONE) { t[i] = WlzAssignObject( WlzUnion2(o[0], t[WLZ_RCCTOIDX_O1F], &errNum), NULL); } break; default: errNum = WLZ_ERR_PARAM_DATA; break; } } } else { errNum = WLZ_ERR_PARAM_DATA; } return(errNum); }
/*! * \return Thresholded object or NULL on error. * \ingroup WlzThreshold * \brief Hysteresis thresholds the given Woolz object. * Values are in the domain of the hysteresis threshold'd * object if they are above/below the primary threshold * or above/below the secondary threshold and connected * to values above/below the primary threshold. * \param srcObj Object to be thresholded. * \param pThrV Primary hysteresis threshold * value. * \param sThrV Threshold for above or below * values. * \param hilo Threshold for above or below * values. * \param con Connectivity to examine for * hysteresis. * \param dstErr Destination error pointer, may * be null. */ WlzObject *WlzHyThreshold(WlzObject *srcObj, WlzPixelV pThrV, WlzPixelV sThrV, WlzThresholdType hilo, WlzConnectType con, WlzErrorNum *dstErr) { int simpleThr = 0; WlzPixelV tmpV; WlzObject *dstObj = NULL, *pThrObj = NULL, *sThrObj = NULL, *dThrObj = NULL, *iThrObj = NULL, *uThrObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; if(srcObj == NULL) { errNum = WLZ_ERR_OBJECT_NULL; } else if(srcObj->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } else if(srcObj->values.core == NULL) { errNum = WLZ_ERR_VALUES_NULL; } else { if(con == WLZ_0_CONNECTED) { simpleThr = 1; } else { if((errNum = WlzValueConvertPixel(&tmpV, sThrV, pThrV.type)) == WLZ_ERR_NONE) { switch(pThrV.type) { case WLZ_GREY_INT: if(tmpV.v.inv == pThrV.v.inv) { simpleThr = 1; } break; case WLZ_GREY_SHORT: if(tmpV.v.shv == pThrV.v.shv) { simpleThr = 1; } break; case WLZ_GREY_UBYTE: if(tmpV.v.ubv == pThrV.v.ubv) { simpleThr = 1; } break; case WLZ_GREY_FLOAT: if(fabs(tmpV.v.flv - pThrV.v.flv) <= FLT_EPSILON) { simpleThr = 1; } break; case WLZ_GREY_DOUBLE: if(fabs(tmpV.v.dbv - pThrV.v.dbv) <= DBL_EPSILON) { simpleThr = 1; } break; case WLZ_GREY_RGBA: if( tmpV.v.rgbv == pThrV.v.rgbv ) { simpleThr = 1; } break; default: errNum = WLZ_ERR_GREY_TYPE; break; } } } } if(errNum == WLZ_ERR_NONE) { if(simpleThr) { dstObj = WlzThreshold(srcObj, pThrV, hilo, &errNum); } else { pThrObj = WlzThreshold(srcObj, pThrV, hilo, &errNum); if(errNum == WLZ_ERR_NONE) { if(pThrObj->type == WLZ_EMPTY_OBJ) { dstObj = pThrObj; pThrObj = NULL; } else { sThrObj = WlzThreshold(srcObj, sThrV, hilo, &errNum); if(errNum == WLZ_ERR_NONE) { if(sThrObj->type == WLZ_EMPTY_OBJ) { dstObj = pThrObj; pThrObj = NULL; } else { dThrObj = WlzDilation(pThrObj, con, &errNum); if(errNum == WLZ_ERR_NONE) { iThrObj = WlzIntersect2(dThrObj, sThrObj, &errNum); } if(errNum == WLZ_ERR_NONE) { uThrObj = WlzUnion2(pThrObj, iThrObj, &errNum); } if(errNum == WLZ_ERR_NONE) { dstObj = WlzMakeMain(uThrObj->type, uThrObj->domain, srcObj->values, srcObj->plist, srcObj, &errNum); } if(dThrObj) { WlzFreeObj(dThrObj); } if(iThrObj) { WlzFreeObj(iThrObj); } if(uThrObj) { WlzFreeObj(uThrObj); } } if(sThrObj) { WlzFreeObj(sThrObj); } if(pThrObj) { WlzFreeObj(pThrObj); } } } } } } if(dstErr) { *dstErr = errNum; } return(dstObj); }
/*! * \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); }
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; }