/*! * \return Woolz error code. * \ingroup WlzGeoModel * \brief Copies elements from the given model to the cut model on * the condition that the elements do not intersects any * elements of the knide model. * All models are assumed to be valid 3D models. * \param given Given model. * \param knife Knife model. * \param dstErr Destination error pointer, may be NULL. */ static WlzErrorNum WlzGMModelCut3D(WlzGMModel *cut, WlzGMModel *given, WlzGMModel *knife) { WlzGMGridWSp3D *kGrid = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; if((cut == NULL) || (knife == NULL) || (given == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } else { int cutDim, givenDim, knifeDim; cutDim = WlzGMModelGetDimension(cut, NULL); givenDim = WlzGMModelGetDimension(given, NULL); knifeDim = WlzGMModelGetDimension(knife, NULL); if((cutDim != 3) || (givenDim != 3) || (knifeDim != 3)) { errNum = WLZ_ERR_DOMAIN_TYPE; } } /* Create a cell grid workspace for the knife model. */ if(errNum == WLZ_ERR_NONE) { kGrid = WlzGeoModelGridWSpNew3D(knife, WLZ_GMELM_FACE, &errNum); } /* For each face of the given model, if it does not intersect a face of * the knife model then add it to the new cut model. */ if(errNum == WLZ_ERR_NONE) { int idF, idX, idY, idZ, gMaxF; AlcVector *gFVec; gMaxF = given->res.face.numIdx; gFVec = given->res.face.vec; for(idF = 0; idF < gMaxF; ++idF) { WlzGMFace *gFace; gFace = (WlzGMFace *)AlcVectorItemGet(gFVec, idF); if(gFace->idx >= 0) { int add = 1; WlzDVertex3 gVtx[3]; WlzIBox3 cBox; WlzDBox3 fBox; (void )WlzGMFaceGetG3D(gFace, gVtx + 0, gVtx + 1, gVtx + 2); fBox = WlzBoundingBoxVtx3D(3, gVtx, NULL); cBox = WlzGeoModelGridCellsInDBox(kGrid, fBox); /* For each cell that the bonding box intersects. */ for(idZ = cBox.zMin; idZ <= cBox.zMax; ++idZ) { WlzDVertex3 kCellMin, kCellMax; kCellMin.vtZ = kGrid->org.vtZ + kGrid->cellSz * idZ; kCellMax.vtZ = kGrid->org.vtZ + kGrid->cellSz * (idZ + 1); for(idY = cBox.yMin; idY <= cBox.yMax; ++idY) { kCellMin.vtY = kGrid->org.vtY + kGrid->cellSz * idY; kCellMax.vtY = kGrid->org.vtY + kGrid->cellSz * (idY + 1); for(idX = cBox.xMin; idX <= cBox.xMax; ++idX) { WlzDVertex3 kVtx[3]; WlzGMGridWSpCell3D *kCell; kCellMin.vtX = kGrid->org.vtX + kGrid->cellSz * idX; kCellMax.vtX = kGrid->org.vtX + kGrid->cellSz * (idX + 1); /* Test if the face of the given model intersects the faces * of the knife grid cells. */ kCell = *(*(*(kGrid->cells + idZ) + idY) + idX); while(kCell != NULL) { WlzGMFace *kFace; kFace = kCell->elem.face; (void )WlzGMFaceGetG3D(kFace, kVtx + 0, kVtx + 1, kVtx + 2); if(WlzGeomTriangleTriangleIntersect3D( gVtx[0], gVtx[1], gVtx[2], kVtx[0], kVtx[1], kVtx[2]) != 0) { add = 0; idX = cBox.xMax + 1; /* Break from these loops. */ idY = cBox.yMax + 1; idZ = cBox.zMax + 1; break; } if(errNum != WLZ_ERR_NONE) { goto RETURN; } kCell = kCell->next; } } } } if(add) { /* Given model face does not intersect the knife model face * so just add it to the cut model. */ errNum = WlzGMModelConstructSimplex3D(cut, gVtx); } } } } RETURN: if(kGrid) { (void )WlzGeoModelGridFree3D(kGrid); } return(errNum); }
/*! * \return Object read from file. * \ingroup WlzExtFF * \brief Reads a Woolz object from the given stream using the * GRUMMP vmesh tetrahedral mesh file format. * \param fP Input file stream. * \param dstErr Destination error number ptr, may be * NULL. */ WlzObject *WlzEffReadObjEMT(FILE *fP, WlzErrorNum *dstErr) { int nElm = 0, nNod = 0; char *str; WlzDVertex3 *vBuf = NULL; WlzCMesh3D *mesh = NULL; WlzObject *obj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; char cBuf[256]; if(fP == NULL) { errNum = WLZ_ERR_PARAM_NULL; } else { int idC = 0; /* Optional records may be comments starting with a '#' but apart * from these the first line should have a single integer specifying * the number of nodes. */ do { if((str = WlzEffReadObjEMTRec(fP, cBuf, 256)) == NULL) { errNum = WLZ_ERR_READ_INCOMPLETE; } else if(*str != '#') { if((idC = sscanf(str, "%d", &nNod)) != 1) { errNum = WLZ_ERR_READ_INCOMPLETE; } } } while((errNum == WLZ_ERR_NONE) && (idC != 1)); } /* Check for reasonable number of nodes. */ if(errNum == WLZ_ERR_NONE) { if(nNod <= 0) { errNum = WLZ_ERR_READ_INCOMPLETE; } } /* Create a new 3D constrained mesh. */ if(errNum == WLZ_ERR_NONE) { mesh = WlzCMeshNew3D(&errNum); } /* Read in the node positions into a temporary buffer computing their * bounding box and then create the nodes. */ if(errNum == WLZ_ERR_NONE) { if((vBuf = AlcMalloc(sizeof(WlzDVertex3) * nNod)) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } if(errNum == WLZ_ERR_NONE) { int idN = 0; while(idN < nNod) { if((str = WlzEffReadObjEMTRec(fP, cBuf, 256)) == NULL) { errNum = WLZ_ERR_READ_INCOMPLETE; break; } else if(*str != '#') { if(sscanf(str, "%lg %lg %lg", &(vBuf[idN].vtX), &(vBuf[idN].vtY), &(vBuf[idN].vtZ)) != 3) { errNum = WLZ_ERR_READ_INCOMPLETE; break; } ++idN; } } } if(errNum == WLZ_ERR_NONE) { mesh->bBox = WlzBoundingBoxVtx3D(nNod, vBuf, NULL); if(AlcVectorExtendAndGet(mesh->res.nod.vec, nNod) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } if(errNum == WLZ_ERR_NONE) { errNum = WlzCMeshReassignGridCells3D(mesh, nNod); } if(errNum == WLZ_ERR_NONE) { int idN; for(idN = 0; idN < nNod; ++idN) { (void )WlzCMeshNewNod3D(mesh, vBuf[idN], NULL); } } AlcFree(vBuf); /* Read the number of elements in the mesh. */ if(errNum == WLZ_ERR_NONE) { int idC = 0; /* Optional records may be comments starting with a '#' but apart * from these the first line should have a single integer specifying * the number of nodes. */ do { if((str = WlzEffReadObjEMTRec(fP, cBuf, 256)) == NULL) { errNum = WLZ_ERR_READ_INCOMPLETE; } else if(*str != '#') { if((idC = sscanf(str, "%d", &nElm)) != 1) { errNum = WLZ_ERR_READ_INCOMPLETE; } } } while((errNum == WLZ_ERR_NONE) && (idC != 1)); } /* Check for reasonable number of elements. */ if(errNum == WLZ_ERR_NONE) { if(nElm <= 0) { errNum = WLZ_ERR_READ_INCOMPLETE; } } /* Allocate room for the elements in the mesh. */ if(errNum == WLZ_ERR_NONE) { if(AlcVectorExtendAndGet(mesh->res.elm.vec, nElm) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } /* Read the elements adding them to the mesh. */ if(errNum == WLZ_ERR_NONE) { int idE = 0; while(idE < nElm) { if((str = WlzEffReadObjEMTRec(fP, cBuf, 256)) == NULL) { errNum = WLZ_ERR_READ_INCOMPLETE; break; } else if(*str != '#') { int dummyI; int nIdx[4]; WlzCMeshNod3D *nBuf[4]; if((sscanf(str, "%d %d %d %d %d", &dummyI, &(nIdx[0]), &(nIdx[1]), &(nIdx[2]), &(nIdx[3])) != 5) || (nIdx[0] <= 0) || (nIdx[0] > nNod) || (nIdx[1] <= 0) || (nIdx[1] > nNod) || (nIdx[2] <= 0) || (nIdx[2] > nNod) || (nIdx[3] <= 0) || (nIdx[3] > nNod)) { errNum = WLZ_ERR_READ_INCOMPLETE; break; } else { int idN; for(idN = 0; idN < 4; ++idN) { nBuf[idN] = (WlzCMeshNod3D *) AlcVectorItemGet(mesh->res.nod.vec, nIdx[idN] - 1); } (void )WlzCMeshNewElm3D(mesh, nBuf[0], nBuf[1], nBuf[3], nBuf[2], 1, &errNum); if(errNum != WLZ_ERR_NONE) { break; } ++idE; } } } } /* Ignore the boundary faces in the file, they're redundant information. */ if(errNum == WLZ_ERR_NONE) { WlzDomain dom; WlzValues val; val.core = NULL; dom.cm3 = mesh; WlzCMeshDelUnusedNodes3D(mesh); WlzCMeshUpdateMaxSqEdgLen3D(mesh); obj = WlzMakeMain(WLZ_CMESH_3D, dom, val, NULL, NULL, &errNum); } if(errNum != WLZ_ERR_NONE) { (void )WlzCMeshFree3D(mesh); } if(dstErr) { *dstErr = errNum; } return(obj); }
int main(int argc, char **argv) { int dim = 3, nSPts, nIPts, nOPts, option, planeFlg = 0, planeIdx = 0, ok = 1, usage = 0; double delta, tau, sAlpha, iAlpha, oAlpha, iDist, oDist, samFac = 1.0; WlzObject *dObj = NULL; WlzBox bBox0, bBox1; WlzVertexP sPts, iPts, oPts; WlzPixelV bgdV; WlzDomain outDom; WlzValues dumVal; WlzObject *outObj = NULL; FILE *fP = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; char *sFileStr, *iFileStr, *oFileStr, *outObjFileStr; const char *errMsg; static char optList[] = "h23o:S:I:O:a:b:c:d:e:D:M:T:X:", defFileStr[] = "-"; bgdV.type = WLZ_GREY_DOUBLE; outDom.core = NULL; dumVal.core = NULL; sPts.v = iPts.v = oPts.v = NULL; /* Parse the command line. */ opterr = 0; sFileStr = iFileStr = oFileStr = outObjFileStr = defFileStr; while(ok && ((option = getopt(argc, argv, optList)) != -1)) { switch(option) { case '2': dim = 2; break; case '3': dim = 3; break; case 'o': outObjFileStr = optarg; break; case 'S': sFileStr = optarg; break; case 'I': iFileStr = optarg; break; case 'O': oFileStr = optarg; break; case 'a': if(sscanf(optarg, "%lg", &sAlpha) != 1) { usage = 1; } break; case 'b': if(sscanf(optarg, "%lg", &iAlpha) != 1) { usage = 1; } break; case 'c': if(sscanf(optarg, "%lg", &oAlpha) != 1) { usage = 1; } break; case 'd': if(sscanf(optarg, "%lg", &iDist) != 1) { usage = 1; } break; case 'e': if(sscanf(optarg, "%lg", &oDist) != 1) { usage = 1; } break; case 'D': if(sscanf(optarg, "%lg", &delta) != 1) { usage = 1; } break; case 'M': if(sscanf(optarg, "%lg", &samFac) != 1) { usage = 1; } break; case 'T': if(sscanf(optarg, "%lg", &tau) != 1) { usage = 1; } break; case 'X': if(sscanf(optarg, "%d", &planeIdx) != 1) { usage = 1; } else { planeFlg = 1; } break; case 'h': default: usage = 1; break; } } if(ok && (optind < argc)) { usage = 1; } if((outObjFileStr == NULL) || (*outObjFileStr == '\0') || (sFileStr == NULL) || (*sFileStr == '\0') || (iFileStr == NULL) || (*iFileStr == '\0') || (oFileStr == NULL) || (*oFileStr == '\0')) { usage = 1; } ok = ok && !usage; /* Read the surface, inside and outside points. */ if(ok) { if(strcmp(sFileStr, "-")) { if((fP = fopen(sFileStr, "r")) == NULL) { ok = 0; errNum = WLZ_ERR_READ_EOF; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to open surface points file %s (%s).\n", *argv, sFileStr, errMsg); } } else { fP = stdin; } } if(ok) { sPts = WlzCFPReadVtxArray(fP, dim, &nSPts, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to read points from file %s (%s).\n", *argv, sFileStr, errMsg); } } if(fP) { if(strcmp(sFileStr, "-")) { (void )fclose(fP); } fP = NULL; } if(ok) { if(strcmp(sFileStr, "-")) { if((fP = fopen(iFileStr, "r")) == NULL) { ok = 0; errNum = WLZ_ERR_READ_EOF; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to open inside points file %s (%s).\n", *argv, iFileStr, errMsg); } } else { fP = stdin; } } if(ok) { iPts = WlzCFPReadVtxArray(fP, dim, &nIPts, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to read points from file %s (%s).\n", *argv, sFileStr, errMsg); } } if(fP) { if(strcmp(iFileStr, "-")) { (void )fclose(fP); } fP = NULL; } if(ok) { if(strcmp(sFileStr, "-")) { if((fP = fopen(oFileStr, "r")) == NULL) { ok = 0; errNum = WLZ_ERR_READ_EOF; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to open outside points file %s (%s).\n", *argv, oFileStr, errMsg); } } else { fP = stdin; } } if(ok) { oPts = WlzCFPReadVtxArray(fP, dim, &nOPts, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to read points from file %s (%s).\n", *argv, oFileStr, errMsg); } } if(fP) { if(strcmp(oFileStr, "-")) { (void )fclose(fP); } fP = NULL; } if(ok) { bgdV.v.dbv = oDist; bgdV.type = WLZ_GREY_DOUBLE; switch(dim) { case 2: errNum = WLZ_ERR_UNIMPLEMENTED; break; case 3: /* Compute ounding box of points. */ bBox0.d3 = WlzBoundingBoxVtx3D(nSPts, sPts.d3, NULL); bBox1.d3 = WlzBoundingBoxVtx3D(nIPts, iPts.d3, NULL); bBox0.d3 = WlzBoundingBoxUnion3D(bBox0.d3, bBox1.d3); bBox1.d3 = WlzBoundingBoxVtx3D(nOPts, oPts.d3, NULL); bBox0.d3 = WlzBoundingBoxUnion3D(bBox0.d3, bBox1.d3); /* Create distance object that will be used to accumulate distance values. If only a 2D contour is required then create a distance object with a single plane. */ dObj = WlzMakeCuboid((planeFlg)? planeIdx: (int )floor(bBox0.d3.zMin) - 2, (planeFlg)? planeIdx: (int )ceil(bBox0.d3.zMax) + 2, (int )floor(bBox0.d3.yMin) - 2, (int )ceil(bBox0.d3.yMax) + 2, (int )floor(bBox0.d3.xMin) - 2, (int )ceil(bBox0.d3.xMax) + 2, WLZ_GREY_DOUBLE, bgdV, NULL, NULL, &errNum); if((dObj == NULL) || (errNum != WLZ_ERR_NONE)) { ok = 0; if(errNum == WLZ_ERR_NONE) { errNum = WLZ_ERR_MEM_ALLOC; } (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to allocate distance object (%s).\n", *argv, errMsg); } /* Compute contour using given distance object. */ outDom.ctr = WlzContourFromPoints(dObj, WLZ_VERTEX_D3, nSPts, sPts, sAlpha, nIPts, iPts, iDist, iAlpha, nOPts, oPts, oDist, oAlpha, delta, tau, samFac, &errNum); WlzFreeObj(dObj); break; } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to compute contour from points (%s).\n", *argv, errMsg); } } /* Compute the contour from the point sets. */ if(ok) { outObj = WlzMakeMain(WLZ_CONTOUR, outDom, dumVal, NULL, NULL, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: Failed to create output woolz object (%s).\n", argv[0], errMsg); } if(outObj) { outDom.core = NULL; } } /* Write out the contour object. */ if(ok) { if(strcmp(outObjFileStr, "-")) { if((fP = fopen(outObjFileStr, "w")) == NULL) { ok = 0; errNum = WLZ_ERR_WRITE_EOF; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to open output object file %s (%s).\n", *argv, outObjFileStr, errMsg); } } else { fP = stdout; } if(ok) { if((errNum = WlzWriteObj(fP, outObj)) != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to write output object to file %s (%s).\n", *argv, outObjFileStr, errMsg); } } if(strcmp(outObjFileStr, "-")) { (void )fclose(fP); } fP = NULL; } AlcFree(sPts.v); AlcFree(iPts.v); AlcFree(oPts.v); (void )WlzFreeDomain(outDom); (void )WlzFreeObj(outObj); /* Tidy up and exit. */ if(usage) { (void )fprintf(stderr, "Usage: %s%s", *argv, "WlzContourFromPoints [-h] [-2] [-3] [-o<out object>]\n" " [-S<surface points file>]\n" " [-I<inside points file>] [-O<outside points file>]\n" " [-a#] [-b#] [-c#] [-d#] [-e#] [-D#] [-M#] [-T#]\n" " [-X#]\n" "Options:\n" " -h Print this usage message.\n" " -2 Points are 2D and contour is a curve in 2D.\n" " -3 Points are 3D and contour is a surface in 3D.\n" " -o Output object file name.\n" " -S On surface points file.\n" " -I Inside points file.\n" " -O Outside points file.\n" " -a Surface points alpha value.\n" " -b Inside points alpha value.\n" " -c Outside points alpha value.\n" " -d Inside points distance value.\n" " -e Outside points distance value.\n" " -D Delta multiorder spline soothing value.\n" " -M Distance object sampling factor (can be used to reduce the\n" " number of surface facets.)\n" " -T Tau multiorder spline soothing value.\n" " -X Plane for 2D contour from 3D point sets.\n" "Computes a contour from three point sets: inside; outside and on a\n" "surface.\n" "All points are given using the following ascii format:\n" " <vertex x> <vertex y> [<vertex z>]\n"); } return(!ok); }