Ejemplo n.º 1
0
/*!
* \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);
}
Ejemplo n.º 2
0
/*!
* \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);
}
Ejemplo n.º 3
0
/*!
* \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);
}
Ejemplo n.º 4
0
/*!
* \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);
}
Ejemplo n.º 5
0
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);
}