/*! * \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 lie within the * knide domain. * It is assumed that the model is a valid 3D model and that * the knife is a valid 3D (plane) domain object. * \param given Given model. * \param knife Knife object. * \param dstErr Destination error pointer, may be NULL. */ static WlzErrorNum WlzGMModelCutDom3D(WlzGMModel *cut, WlzGMModel *given, WlzObject *knife) { WlzPlaneDomain *kDom = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; if(knife == NULL) { errNum = WLZ_ERR_OBJECT_NULL; } else if((cut == NULL) || (given == NULL) || ((kDom = knife->domain.p) == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } else { int idF, gMaxF; AlcVector *gFVec; /* 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. */ 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; WlzDVertex3 v[3]; (void )WlzGMFaceGetG3D(gFace, v + 0, v + 1, v + 2); add = (WlzInsideDomain3D(kDom, v[0].vtZ, v[0].vtY, v[0].vtX, NULL) == 0) && (WlzInsideDomain3D(kDom, v[1].vtZ, v[1].vtY, v[1].vtX, NULL) == 0) && (WlzInsideDomain3D(kDom, v[2].vtZ, v[2].vtY, v[2].vtX, NULL) == 0); if(add) { /* Given model face does not intersect the knife domain so add it * to the cut model. */ errNum = WlzGMModelConstructSimplex3D(cut, v); } } } } 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 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); }
int main(int argc, char **argv) { int idR, nRec, nFld, option, ok = 1, usage = 0; FILE *fP = NULL; char *inFileStr, *outObjFileStr; double **bufD = NULL; WlzGMModelType modelType = WLZ_GMMOD_2D; WlzDVertex2 bufD2[2]; WlzDVertex3 bufD3[3]; WlzObject *outObj = NULL; WlzDomain ctrDom; WlzValues dumVal; WlzGMModel *model = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; const char *errMsgStr; static char optList[] = "23diho:"; const char outObjFileStrDef[] = "-", inFileStrDef[] = "-"; opterr = 0; ctrDom.core = NULL; dumVal.core = NULL; outObjFileStr = (char *)outObjFileStrDef; inFileStr = (char *)inFileStrDef; while(ok && ((option = getopt(argc, argv, optList)) != -1)) { switch(option) { case '2': switch(modelType) { case WLZ_GMMOD_2I: /* FALLTHROUGH */ case WLZ_GMMOD_2D: break; case WLZ_GMMOD_3I: modelType = WLZ_GMMOD_2I; break; case WLZ_GMMOD_3D: modelType = WLZ_GMMOD_2D; break; default: break; } break; case '3': switch(modelType) { case WLZ_GMMOD_2I: modelType = WLZ_GMMOD_3I; break; case WLZ_GMMOD_2D: modelType = WLZ_GMMOD_3D; break; case WLZ_GMMOD_3I: /* FALLTHROUGH */ case WLZ_GMMOD_3D: break; default: break; } break; case 'd': switch(modelType) { case WLZ_GMMOD_2I: modelType = WLZ_GMMOD_2D; case WLZ_GMMOD_2D: break; case WLZ_GMMOD_3I: modelType = WLZ_GMMOD_3D; break; case WLZ_GMMOD_3D: break; default: break; } break; case 'i': switch(modelType) { case WLZ_GMMOD_2I: break; case WLZ_GMMOD_2D: modelType = WLZ_GMMOD_2I; break; case WLZ_GMMOD_3I: break; case WLZ_GMMOD_3D: modelType = WLZ_GMMOD_3I; break; default: break; } break; case 'h': usage = 1; ok = 0; break; case 'o': outObjFileStr = optarg; break; default: usage = 1; ok = 0; break; } } if(ok) { if((inFileStr == NULL) || (*inFileStr == '\0') || (outObjFileStr == NULL) || (*outObjFileStr == '\0')) { ok = 0; usage = 1; } if(ok && (optind < argc)) { if((optind + 1) != argc) { usage = 1; ok = 0; } else { inFileStr = *(argv + optind); } } } if(ok) { if((inFileStr == NULL) || (*inFileStr == '\0') || ((fP = (strcmp(inFileStr, "-")? fopen(inFileStr, "r"): stdin)) == NULL) || (AlcDouble2ReadAsci(fP, &bufD, (size_t *) &nRec, (size_t *) &nFld) != ALC_ER_NONE)) { ok = 0; (void )fprintf(stderr, "%s: failed to read from file %s\n", *argv, inFileStr); } if(fP && strcmp(inFileStr, "-")) { fclose(fP); } } if(ok) { ctrDom.ctr = WlzMakeContour(&errNum); if(errNum == WLZ_ERR_NONE) { ctrDom.ctr->model = model = WlzAssignGMModel( WlzGMModelNew(modelType, 0, 0, &errNum), NULL); } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s: Failed to make contour (%s).\n", argv[0], errMsgStr); } } if(ok) { switch(modelType) { case WLZ_GMMOD_2I: /* FALLTHROUGH */ case WLZ_GMMOD_2D: if(nFld != 4) { ok = 0; } break; case WLZ_GMMOD_3I: /* FALLTHROUGH */ case WLZ_GMMOD_3D: if(nFld != 9) { ok = 0; } break; default: break; } if(ok == 0) { (void )fprintf(stderr, "%s: Failed to read simplicies (syntax error).\n", argv[0]); } } if(ok) { idR = 0; while((errNum == WLZ_ERR_NONE) && (idR < nRec)) { switch(modelType) { case WLZ_GMMOD_2I: /* FALLTHROUGH */ case WLZ_GMMOD_2D: bufD2[0].vtX = *(*(bufD + idR) + 0); bufD2[0].vtY = *(*(bufD + idR) + 1); bufD2[1].vtX = *(*(bufD + idR) + 2); bufD2[1].vtY = *(*(bufD + idR) + 3); errNum = WlzGMModelConstructSimplex2D(model, bufD2); break; case WLZ_GMMOD_3I: /* FALLTHROUGH */ case WLZ_GMMOD_3D: bufD3[0].vtX = *(*(bufD + idR) + 0); bufD3[0].vtY = *(*(bufD + idR) + 1); bufD3[0].vtZ = *(*(bufD + idR) + 2); bufD3[1].vtX = *(*(bufD + idR) + 3); bufD3[1].vtY = *(*(bufD + idR) + 4); bufD3[1].vtZ = *(*(bufD + idR) + 5); bufD3[2].vtX = *(*(bufD + idR) + 6); bufD3[2].vtY = *(*(bufD + idR) + 7); bufD3[2].vtZ = *(*(bufD + idR) + 8); errNum = WlzGMModelConstructSimplex3D(model, bufD3); break; default: break; } ++idR; } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s: Failed to build model (%s).\n", argv[0], errMsgStr); } } if(ok) { if((fP = (strcmp(outObjFileStr, "-")? fopen(outObjFileStr, "w"): stdout)) == NULL) { ok = 0; (void )fprintf(stderr, "%s: Failed to open output file %s.\n", argv[0], outObjFileStr); } } if(ok) { outObj = WlzMakeMain(WLZ_CONTOUR, ctrDom, dumVal, NULL, NULL, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s: Failed to create output woolz object (%s).\n", argv[0], errMsgStr); } } if(ok) { errNum = WlzWriteObj(fP, outObj); 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(bufD) { (void )AlcDouble2Free(bufD); } if(outObj) { (void )WlzFreeObj(outObj); } else if(ctrDom.ctr) { (void )WlzFreeContour(ctrDom.ctr); } if(fP && strcmp(outObjFileStr, "-")) { (void )fclose(fP); } if(usage) { (void )fprintf(stderr, "Usage: %s%s%s%sExample: %s%s", *argv, " [-o<output object>] [-h] [-2] [-3] [-d] [-i] [-o#] \n" " [<input data>]\n" "Version: ", WlzVersion(), "\n" "Options:\n" " -h Prints this usage information.\n" " -o Output object file name.\n" " -2 Build a 2D contour.\n" " -3 Build a 3D contour.\n" " -d Build a contour with double floating point geometry.\n" " -i Build a contour with integer geometry.\n" "Builds a contour from the given input simplicies.\n" "The input data are read from stdin and the output object is written\n" "to stdout unless filenames are given.\n", *argv, " -d -2 -o tri.wlz\n" "0.0 0.0 1.0 0.0\n" "1.0 0.0 0.5 0.866025\n" "0.5 0.866025 0.0 0.0\n" "<eof>\n" "A 2D triangle is built from the three simplicies read from the\n" "standard input and the contour is written to tri.wlz.\n"); } return(!ok); }