/*! * \ingroup WlzAllocation * \brief Free's a WlzContour data structure. * * \return Error number, values: WLZ_ERR_NONE, WLZ_ERR_DOMAIN_NULL, WLZ_ERR_DOMAIN_TYPE and from WlzUnlink(). * \param ctr Contour to be freed. * \par Source: * WlzFreeSpace.c */ WlzErrorNum WlzFreeContour(WlzContour *ctr) { WlzErrorNum errNum = WLZ_ERR_NONE; if(ctr == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } else if(ctr->type != WLZ_CONTOUR) { errNum = WLZ_ERR_DOMAIN_TYPE; } else { if(WlzUnlink(&(ctr->linkcount), &errNum)) { if(ctr->model && WlzUnlink(&(ctr->model->linkcount), &errNum)) { (void )WlzGMModelFree(ctr->model); } AlcFree((void *)ctr); } } return(errNum); }
/*! * \return Object read from file. * \ingroup WlzExtFF * \brief Reads a Woolz object from the given stream using the * stereolithography stl surface file format. This format * has surface definitions defined by a name and a collection * of facet normals and loops. Each facet loop has the * cooordinates of it's vertices. * Only triangulated surface models can be read and all solids * will be treated as one. * \param fP Input file stream. * \param dstErr Destination error number ptr, may be * NULL. */ WlzObject *WlzEffReadObjStl(FILE *fP, WlzErrorNum *dstErr) { int vCnt = 0, inSolid = 0, inFacet = 0, inLoop = 0; char *sav, *str, *tok; WlzDVertex3 vBuf[3]; WlzGMModel *model = NULL; WlzObject *obj = NULL; WlzDomain dom; WlzValues val; WlzErrorNum errNum = WLZ_ERR_NONE; char cBuf[256]; dom.core = NULL; val.core = NULL; if(fP == NULL) { errNum = WLZ_ERR_PARAM_NULL; } else { model = WlzGMModelNew(WLZ_GMMOD_3D, 0, 0, &errNum); } while((errNum == WLZ_ERR_NONE) && ((str = WlzEffReadObjStlRec(fP, cBuf, 256)) != NULL)) { if((tok = strtok_r(str, " \t", &sav)) != NULL) { if(strncmp(tok, "solid", 5) == 0) { if(inSolid == 0) { inSolid = 1; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "facet", 5) == 0) { if((inSolid == 1) && (inFacet == 0)) { inFacet = 1; /* Normal vector is ignored. */ } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "outer", 5) == 0) { if(((tok = strtok_r(NULL, " \t", &sav)) == NULL) || (strncmp(tok, "loop", 4) != 0) || (inSolid == 0) || (inFacet == 0) || (inLoop != 0)) { errNum = WLZ_ERR_READ_INCOMPLETE; } else { vCnt = 0; inLoop = 1; } } else if(strncmp(tok, "vertex", 6) == 0) { char *pTok[3]; if((vCnt < 3) && ((pTok[0] = strtok_r(NULL, " \t", &sav)) != NULL) && ((pTok[1] = strtok_r(NULL, " \t", &sav)) != NULL) && ((pTok[2] = strtok_r(NULL, " \t", &sav)) != NULL) && (sscanf(pTok[0], "%lg", &(vBuf[vCnt].vtX)) == 1) && (sscanf(pTok[1], "%lg", &(vBuf[vCnt].vtY)) == 1) && (sscanf(pTok[2], "%lg", &(vBuf[vCnt].vtZ)) == 1)) { ++vCnt; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endloop", 7) == 0) { if(inLoop == 1) { inLoop = 0; if(vCnt == 3) { errNum = WlzGMModelConstructSimplex3D(model, vBuf); } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endfacet", 8) == 0) { if(inFacet == 1) { inFacet = 0; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endsolid", 8) == 0) { if(inSolid == 1) { inSolid = 0; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } } } /* Create the Woolz object. */ if(errNum == WLZ_ERR_NONE) { if((dom.ctr = WlzMakeContour(&errNum)) != NULL) { dom.ctr->model = WlzAssignGMModel(model, NULL); obj = WlzMakeMain(WLZ_CONTOUR, dom, val, NULL, NULL, &errNum); } } if(errNum != WLZ_ERR_NONE) { if(obj != NULL) { (void )WlzFreeObj(obj); } else if(dom.ctr != NULL) { (void )WlzFreeContour(dom.ctr); } else { (void )WlzGMModelFree(model); } obj = NULL; } if(dstErr) { *dstErr = errNum; } return(obj); }
/*! * \return Object read from file. * \ingroup WlzExtFF * \brief Reads a Woolz object from the given stream using the * stereolithography stl surface file format. This format * has surface definitions defined by a name and a collection * of facet normals and loops. Each facet loop has the * cooordinates of it's vertices. * Only triangulated surface models can be read and all solids * will be treated as one. * \param fP Input file stream. * \param dstErr Destination error number ptr, may be * NULL. */ WlzObject *WlzEffReadObjStl(FILE *fP, WlzErrorNum *dstErr) { int vCnt = 0, inSolid = 0, inFacet = 0, inLoop = 0; char *sav, *str, *tok; WlzDVertex3 vBuf[3]; WlzGMModel *model = NULL; WlzObject *obj = NULL; WlzDomain dom; WlzValues val; WlzErrorNum errNum = WLZ_ERR_NONE; char buf[256]; dom.core = NULL; val.core = NULL; if(fP == NULL) { errNum = WLZ_ERR_PARAM_NULL; } else { model = WlzGMModelNew(WLZ_GMMOD_3D, 0, 0, &errNum); } /* Read just the first 5 bytes, these will contain the string "solid" if * the STL file is ASCII or something else if binary. */ if(fread(buf, sizeof(char), 5, fP) != 5) { errNum = WLZ_ERR_READ_INCOMPLETE; } else { buf[5] = '\0'; if(strncmp(buf, "solid", 5) != 0) /* Binary not ASCII */ { uint32_t tIdx, nT = 0; WlzGreyV tx = {0}; /* Discard the remaining 75 bytes of the 80 byte header block. */ (void )fread(buf, sizeof(char), 75, fP); /* Read number of triangles. */ if(fread(buf, sizeof(char), 4, fP) != 4) { errNum = WLZ_ERR_READ_INCOMPLETE; } else { tx.bytes[0] = buf[0]; tx.bytes[1] = buf[1]; tx.bytes[2] = buf[2]; tx.bytes[3] = buf[3]; nT = (uint32_t)(tx.inv); if(nT < 1) { errNum = WLZ_ERR_READ_INCOMPLETE; } } for(tIdx = 0; (errNum == WLZ_ERR_NONE) && (tIdx < nT); ++tIdx) { if(fread(buf, sizeof(char), 50, fP) != 50) { errNum = WLZ_ERR_READ_INCOMPLETE; } else { int vIdx; size_t bOff = 12; for(vIdx = 0; vIdx < 3; ++vIdx) { tx.bytes[0] = buf[bOff++]; tx.bytes[1] = buf[bOff++]; tx.bytes[2] = buf[bOff++]; tx.bytes[3] = buf[bOff++]; vBuf[vIdx].vtX = tx.flv; tx.bytes[0] = buf[bOff++]; tx.bytes[1] = buf[bOff++]; tx.bytes[2] = buf[bOff++]; tx.bytes[3] = buf[bOff++]; vBuf[vIdx].vtY = tx.flv; tx.bytes[0] = buf[bOff++]; tx.bytes[1] = buf[bOff++]; tx.bytes[2] = buf[bOff++]; tx.bytes[3] = buf[bOff++]; vBuf[vIdx].vtZ = tx.flv; } errNum = WlzGMModelConstructSimplex3D(model, vBuf); } } } else /* ASCII not binary */ { /* Discard rest of the first line. */ (void )WlzEffReadObjStlRec(fP, buf, 256); /* Read and parse ACSII records. */ inSolid = 1; while((errNum == WLZ_ERR_NONE) && ((str = WlzEffReadObjStlRec(fP, buf, 256)) != NULL)) { if((tok = ALC_STRTOK_R(str, " \t", &sav)) != NULL) { if(strncmp(tok, "solid", 5) == 0) { if(inSolid == 0) { inSolid = 1; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "facet", 5) == 0) { if((inSolid == 1) && (inFacet == 0)) { inFacet = 1; /* Normal vector is ignored. */ } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "outer", 5) == 0) { if(((tok = ALC_STRTOK_R(NULL, " \t", &sav)) == NULL) || (strncmp(tok, "loop", 4) != 0) || (inSolid == 0) || (inFacet == 0) || (inLoop != 0)) { errNum = WLZ_ERR_READ_INCOMPLETE; } else { vCnt = 0; inLoop = 1; } } else if(strncmp(tok, "vertex", 6) == 0) { char *pTok[3]; if((vCnt < 3) && ((pTok[0] = ALC_STRTOK_R(NULL, " \t", &sav)) != NULL) && ((pTok[1] = ALC_STRTOK_R(NULL, " \t", &sav)) != NULL) && ((pTok[2] = ALC_STRTOK_R(NULL, " \t", &sav)) != NULL) && (sscanf(pTok[0], "%lg", &(vBuf[vCnt].vtX)) == 1) && (sscanf(pTok[1], "%lg", &(vBuf[vCnt].vtY)) == 1) && (sscanf(pTok[2], "%lg", &(vBuf[vCnt].vtZ)) == 1)) { ++vCnt; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endloop", 7) == 0) { if(inLoop == 1) { inLoop = 0; if(vCnt == 3) { errNum = WlzGMModelConstructSimplex3D(model, vBuf); } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endfacet", 8) == 0) { if(inFacet == 1) { inFacet = 0; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endsolid", 8) == 0) { if(inSolid == 1) { inSolid = 0; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } } } } } /* Create the Woolz object. */ if(errNum == WLZ_ERR_NONE) { if((dom.ctr = WlzMakeContour(&errNum)) != NULL) { dom.ctr->model = WlzAssignGMModel(model, NULL); obj = WlzMakeMain(WLZ_CONTOUR, dom, val, NULL, NULL, &errNum); } } if(errNum != WLZ_ERR_NONE) { if(obj != NULL) { (void )WlzFreeObj(obj); } else if(dom.ctr != NULL) { (void )WlzFreeContour(dom.ctr); } else { (void )WlzGMModelFree(model); } obj = NULL; } if(dstErr) { *dstErr = errNum; } return(obj); }
/*! * \return A new geometric model. * \ingroup WlzGeoModel * \brief Creates a new geometric model from the given model such * that no elements of the given model which intersect * with elements in the knife model are included. * While this code seems to work for simple models errors * have been seen with complex models. The code for this * function and those static functions that it calls should * be considered "under construction". * \param given Given model. * \param knife Knife model. * \param dstErr Destination error pointer, may be NULL. */ WlzGMModel *WlzGMModelCut(WlzGMModel *given, WlzGMModel *knife, WlzErrorNum *dstErr) { int dim = 2, nBkSz, nHTSz; WlzGMModel *cut = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; const int minBkSz = 1024, minHTSz = 1024; if((given == NULL) || (knife == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } else if(given->type != knife->type) { errNum = WLZ_ERR_DOMAIN_TYPE; } else { switch(given->type) { case WLZ_GMMOD_2I: /* FALLTHROUGH */ case WLZ_GMMOD_2D: /* FALLTHROUGH */ case WLZ_GMMOD_2N: dim = 2; break; case WLZ_GMMOD_3I: /* FALLTHROUGH */ case WLZ_GMMOD_3D: /* FALLTHROUGH */ case WLZ_GMMOD_3N: dim = 3; break; default: errNum = WLZ_ERR_DOMAIN_TYPE; break; } } if(errNum == WLZ_ERR_NONE) { /* Make a new model. */ if((nBkSz = given->res.vertex.numElm / 16) < minBkSz) { nBkSz = minBkSz; } if((nHTSz = given->res.vertex.numElm) < minHTSz) { nHTSz = minHTSz; } cut = WlzGMModelNew(given->type, nBkSz, nHTSz, &errNum); } if(errNum == WLZ_ERR_NONE) { if(dim == 2) { errNum = WlzGMModelCut2D(cut, given, knife); } else /* dim == 3 */ { errNum = WlzGMModelCut3D(cut, given, knife); } if(errNum != WLZ_ERR_NONE) { (void )WlzGMModelFree(cut); cut = NULL; } } if(dstErr) { *dstErr = errNum; } return(cut); }
int main(int argc, char *argv[]) { int ok, option, usage = 0; FILE *fP = NULL; WlzDomain oDom; WlzValues oVal; char *mFileStr, *kFileStr, *oFileStr; WlzObject *mObj = NULL, *kObj = NULL, *oObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; const char *errMsgStr; static char optList[] = "hk:o:"; const char fileStrDef[] = "-"; oDom.core = NULL; oVal.core = NULL; /* Parse the argument list and check for input files. */ opterr = 0; mFileStr = (char *)fileStrDef; kFileStr = (char *)fileStrDef; oFileStr = (char *)fileStrDef; while((option = getopt(argc, argv, optList)) != EOF) { switch(option) { case 'k': kFileStr = optarg; break; case 'o': oFileStr = optarg; break; case 'h': default: usage = 1; break; } } ok = !usage; if(ok && (optind < argc)) { if((optind + 1) != argc) { usage = 1; ok = 0; } else { mFileStr = argv[optind]; } } /* Read the model contour object. */ if(ok) { if((mFileStr == NULL) || (*mFileStr == '\0') || ((fP = (strcmp(mFileStr, "-")? fopen(mFileStr, "r"): stdin)) == NULL) || ((mObj = WlzAssignObject(WlzReadObj(fP, &errNum), NULL)) == NULL)) { ok = 0; (void )fprintf(stderr, "%s: Failed to read model contour object from file %s.\n", argv[0], mFileStr); } if(fP && strcmp(mFileStr, "-")) { (void )fclose(fP); fP = NULL; } } /* Read the knife object. */ if(ok) { if((kFileStr == NULL) || (*kFileStr == '\0') || ((fP = (strcmp(kFileStr, "-")? fopen(kFileStr, "r"): stdin)) == NULL) || ((kObj = WlzAssignObject(WlzReadObj(fP, &errNum), NULL)) == NULL)) { ok = 0; (void )fprintf(stderr, "%s: Failed to read knife contour object from file %s.\n", argv[0], kFileStr); } if(fP && strcmp(kFileStr, "-")) { (void )fclose(fP); fP = NULL; } } if(ok) { oDom.ctr = WlzMakeContour(&errNum); switch(kObj->type) { case WLZ_2D_DOMAINOBJ: /* FALLTHROUGH */ case WLZ_3D_DOMAINOBJ: if(errNum == WLZ_ERR_NONE) { oDom.ctr->model = WlzGMModelCutDom(mObj->domain.ctr->model, kObj, &errNum); } break; case WLZ_CONTOUR: if(errNum == WLZ_ERR_NONE) { oDom.ctr->model = WlzGMModelCut(mObj->domain.ctr->model, kObj->domain.ctr->model, &errNum); } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } if(errNum == WLZ_ERR_NONE) { oObj = WlzMakeMain(WLZ_CONTOUR, oDom, oVal, NULL, NULL, &errNum); } if(errNum != WLZ_ERR_NONE) { if(oDom.ctr != NULL) { (void )WlzFreeContour(oDom.ctr); } else if(oDom.ctr->model != NULL) { (void )WlzGMModelFree(oDom.ctr->model); } ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s: Failed to cut contour (%s).\n", argv[0], errMsgStr); } } if(ok) { if((fP = (strcmp(oFileStr, "-")? fopen(oFileStr, "w"): stdout)) == NULL) { ok = 0; (void )fprintf(stderr, "%s: Failed to open output file %s.\n", argv[0], oFileStr); } } if(ok) { errNum = WlzWriteObj(fP, oObj); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s: Failed to write output object, %s.\n", argv[0], errMsgStr); } } if(fP && strcmp(oFileStr, "-")) { (void )fclose(fP); } (void )WlzFreeObj(mObj); (void )WlzFreeObj(kObj); (void )WlzFreeObj(oObj); if(usage) { (void )fprintf(stderr, "Usage: %s [-k <knife>] [-o <out>] [-h] [<model>]\n" "Given model and knife contours; creates a new contour which consists\n" "of all simplices of the model that do not intersect simplices of the\n" "knife. This may be used to cut into a contour removing sections from\n" "it.\n" "Options are:\n" " -k The knife contour object.\n" " -o Output object file.\n" " -h Help - prints this usage message\n", argv[0]); } return(!ok); }