Example #1
0
void checkCrop3D(struct data *d)
{
	int fn3, startd3, cropd3;

	  checkCrop(d);  // set 2D parameters

	  fn3=d->fn3;


	  /* Check that either fn3 is active */
	  /* If so, make sure it is divisible by 4 */

	  if (!fn3) fn3 = d->nv3*2;
	  else { fn3 /=4; fn3 *=4; }

	  fn3 /= 2;

	startd3 = d->startd3;
	cropd3 = d->cropd3;

	if((startd3 < 0) || (startd3 > fn3-2)){
		d->startd3=0;
		fprintf(stderr,"Warning: setting start of crop 3 to 1\n");
	}
	if(cropd3 < 1) {
		d->cropd3=fn3;
		fprintf(stderr,"Warning: setting crop 3 to %d \n",d->cropd3);
	}
	if(d->startd3 + d->cropd3 > fn3){
		d->cropd3 = fn3-d->startd3;
		fprintf(stderr,"Warning: setting crop 3 to %d  \n",d->cropd3);
	}



}
BufferMapper* OverlayPlaneBase::getTTMMapper(BufferMapper& grallocMapper, struct VideoPayloadBuffer *payload)
{
    buffer_handle_t khandle;
    uint32_t w, h;
    uint32_t yStride, uvStride;
    stride_t stride;
    int srcX, srcY, srcW, srcH;
    int tmp;

    DataBuffer *buf;
    ssize_t index;
    TTMBufferMapper *mapper;
    bool ret;

    if (!payload) {
        ETRACE("invalid payload buffer");
        return 0;
    }

    srcX = grallocMapper.getCrop().x;
    srcY = grallocMapper.getCrop().y;
    srcW = grallocMapper.getCrop().w;
    srcH = grallocMapper.getCrop().h;

    // init ttm buffer
    if (mUseScaledBuffer) {
        khandle = payload->scaling_khandle;
    } else {
        khandle = payload->rotated_buffer_handle;
    }
    index = mTTMBuffers.indexOfKey(khandle);
    if (index < 0) {
        VTRACE("unmapped TTM buffer, will map it");

        if (mUseScaledBuffer) {
            w = payload->scaling_width;
            h = payload->scaling_height;
        } else {
            w = payload->rotated_width;
            h = payload->rotated_height;

            checkCrop(srcX, srcY, srcW, srcH, payload->coded_width, payload->coded_height);
        }

        uint32_t format = grallocMapper.getFormat();
        // this is for sw decode with tiled buffer in landscape mode
        if (payload->tiling)
            format = OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled;

        // calculate stride
        switch (format) {
        case HAL_PIXEL_FORMAT_YV12:
        case HAL_PIXEL_FORMAT_I420:
            uint32_t yStride_align;
            yStride_align = DisplayQuery::getOverlayLumaStrideAlignment(grallocMapper.getFormat());
            if (yStride_align > 0)
            {
                yStride = align_to(align_to(w, 32), yStride_align);
            }
            else
            {
                yStride = align_to(align_to(w, 32), 64);
            }
            uvStride = align_to(yStride >> 1, 64);
            stride.yuv.yStride = yStride;
            stride.yuv.uvStride = uvStride;
            break;
        case HAL_PIXEL_FORMAT_NV12:
            yStride = align_to(align_to(w, 32), 64);
            uvStride = yStride;
            stride.yuv.yStride = yStride;
            stride.yuv.uvStride = uvStride;
            break;
        case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar:
        case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled:
            if (mUseScaledBuffer) {
                stride.yuv.yStride = payload->scaling_luma_stride;
                stride.yuv.uvStride = payload->scaling_chroma_u_stride;
            } else {
               yStride = align_to(align_to(w, 32), 64);
               uvStride = yStride;
               stride.yuv.yStride = yStride;
               stride.yuv.uvStride = uvStride;
            }
            break;
        case HAL_PIXEL_FORMAT_YUY2:
        case HAL_PIXEL_FORMAT_UYVY:
            yStride = align_to((align_to(w, 32) << 1), 64);
            uvStride = 0;
            stride.yuv.yStride = yStride;
            stride.yuv.uvStride = uvStride;
            break;
        }

        DataBuffer buf(khandle);
        // update buffer
        buf.setStride(stride);
        buf.setWidth(w);
        buf.setHeight(h);
        buf.setCrop(srcX, srcY, srcW, srcH);
        buf.setFormat(format);

        // create buffer mapper
        bool res = false;
        do {
            mapper = new TTMBufferMapper(*mWsbm, buf);
            if (!mapper) {
                ETRACE("failed to allocate mapper");
                break;
            }
            // map ttm buffer
            ret = mapper->map();
            if (!ret) {
                ETRACE("failed to map");
                invalidateTTMBuffers();
                ret = mapper->map();
                if (!ret) {
                    ETRACE("failed to remap");
                    break;
                }
            }

            if (mTTMBuffers.size() >= OVERLAY_DATA_BUFFER_COUNT) {
                invalidateTTMBuffers();
            }

            // add mapper
            index = mTTMBuffers.add(khandle, mapper);
            if (index < 0) {
                ETRACE("failed to add TTMMapper");
                break;
            }

            // increase mapper refCount since it is added to mTTMBuffers
            mapper->incRef();
            res = true;
        } while (0);

        if (!res) {
            // error handling
            if (mapper) {
                mapper->unmap();
                delete mapper;
                mapper = NULL;
            }
            return 0;
        }
    } else {
Example #3
0
void
DistanceMapFilter::applyDistanceMapFilter(QString flnm,
					  QList<Vec> clipPos,
					  QList<Vec> clipNormal,
					  QList<CropObject> crops,
					  QList<PathObject> paths,
					  uchar *lut,
					  int chan)
{
  int bpv = 1;
  if (m_voxelType > 0) bpv = 2;
  int nbytes = bpv*m_nY*m_nZ;

  bool trim = (qRound(m_dataSize.x) < m_height ||
	       qRound(m_dataSize.y) < m_width ||
	       qRound(m_dataSize.z) < m_depth);
  bool clipPresent = (clipPos.count() > 0);

  m_cropPresent = false;
  m_tearPresent = false;
  m_blendPresent = false;
  for(int ci=0; ci<m_crops.count(); ci++)
    {
      if (crops[ci].cropType() < CropObject::Tear_Tear)
	m_cropPresent = true;
      else if (crops[ci].cropType() < CropObject::View_Tear)
	m_tearPresent = true;
      else if (m_crops[ci].cropType() > CropObject::Displace_Displace &&
	       m_crops[ci].cropType() < CropObject::Glow_Ball)
	m_blendPresent = true;
    }

  m_pathCropPresent = false;
  m_pathBlendPresent = false;
  for (int i=0; i<m_paths.count(); i++)
    {
      if (m_paths[i].blend()) m_pathBlendPresent = true;
      if (m_paths[i].crop()) m_pathCropPresent = true;
    }

  m_meshLog->moveCursor(QTextCursor::End);
  int d0 = 0;
  int d1 = m_nX-1;
  int d0z = d0 + qRound(m_dataMin.z);
  int d1z = d1 + qRound(m_dataMin.z);

  uchar *opacityVol = new uchar[m_nX*m_nY*m_nZ];
  
  uchar *cropped = new uchar[nbytes];
  uchar *tmp = new uchar[nbytes];

  int i0 = 0;
  for(int i=d0z; i<=d1z; i++)
    {
      m_meshProgress->setValue((int)(100.0*(float)i0/(float)m_nX));
      qApp->processEvents();

      int iv = qBound(0, i, m_depth-1);
      uchar *vslice = m_vfm->getSlice(iv);

      memset(cropped, 0, nbytes);

      if (!trim)
	memcpy(tmp, vslice, nbytes);
      else
	{
	  int wmin = qRound(m_dataMin.y);
	  int hmin = qRound(m_dataMin.x);
	  if (m_voxelType == 0)
	    {
	      for(int w=0; w<m_nY; w++)
		for(int h=0; h<m_nZ; h++)
		  tmp[w*m_nZ + h] = vslice[(wmin+w)*m_height + (hmin+h)];
	    }
	  else
	    {
	      for(int w=0; w<m_nY; w++)
		for(int h=0; h<m_nZ; h++)
		  ((ushort*)tmp)[w*m_nZ + h] = ((ushort*)vslice)[(wmin+w)*m_height + (hmin+h)];
	    }
	}

      int jk = 0;
      for(int j=0; j<m_nY; j++)
	for(int k=0; k<m_nZ; k++)
	  {
	    Vec po = Vec(m_dataMin.x+k, m_dataMin.y+j, iv);
	    bool ok = true;
	    
	    // we don't want to scale before pruning
	    int mop = 0;
	    {
	      Vec pp = po - m_dataMin;
	      int ppi = pp.x/m_pruneLod;
	      int ppj = pp.y/m_pruneLod;
	      int ppk = pp.z/m_pruneLod;
	      ppi = qBound(0, ppi, m_pruneX-1);
	      ppj = qBound(0, ppj, m_pruneY-1);
	      ppk = qBound(0, ppk, m_pruneZ-1);
	      int mopidx = ppk*m_pruneY*m_pruneX + ppj*m_pruneX + ppi;
	      mop = m_pruneData[3*mopidx + chan];
	      ok = (mop > 0);
	    }
	    
	    po *= m_samplingLevel;
	    
	    if (ok && clipPresent)
	      ok = StaticFunctions::getClip(po, clipPos, clipNormal);
	    
	    if (ok && m_cropPresent)
	      ok = checkCrop(po);
	    
	    if (ok && m_pathCropPresent)
	      ok = checkPathCrop(po);
	    
	    if (ok && m_blendPresent)
	      {
		ushort v;
		if (m_voxelType == 0)
		  v = tmp[j*m_nZ + k];
		else
		  v = ((ushort*)tmp)[j*m_nZ + k];
		ok = checkBlend(po, v, lut);
	      }
	    
	    if (ok && m_pathBlendPresent)
	      {
		ushort v;
		if (m_voxelType == 0)
		  v = tmp[j*m_nZ + k];
		else
		  v = ((ushort*)tmp)[j*m_nZ + k];
		ok = checkPathBlend(po, v, lut);
	      }
	    
	    if (ok)
	      cropped[jk] = mop;
	    else
	      cropped[jk] = 0;
	    
	    jk ++;
	  }
      
      if (m_voxelType == 0)
	{
	  for(int j=0; j<m_nY*m_nZ; j++)
	    {
	      if (cropped[j] == 0)
		tmp[j] = 0;
	    }
	}
      else
	{
	  for(int j=0; j<m_nY*m_nZ; j++)
	    {
	      if (cropped[j] == 0)
		((ushort*)tmp)[j] = 0;
	    }
	}
      
      applyOpacity(iv, cropped, lut, tmp);
      memcpy(opacityVol + i0*m_nY*m_nZ, tmp, m_nY*m_nZ);
      
      i0++;
    }
  delete [] tmp;
  delete [] cropped;
  m_meshProgress->setValue(100);
  qApp->processEvents();

  //------------
  if (m_tearPresent)
    {
      uchar *data0 = new uchar[m_nX*m_nY*m_nZ];
      memcpy(data0, opacityVol, m_nX*m_nY*m_nZ);
      applyTear(d0, d1, 0,
		data0, opacityVol, false);
      
      delete [] data0;
    }


  typedef uchar PixelType;
  const unsigned int Dimension = 3;
  typedef itk::Image< PixelType, Dimension > ImageType;

  ImageType::IndexType start;
  start.Fill(0);

  ImageType::SizeType size;
  size[0] = m_nZ;
  size[1] = m_nY;
  size[2] = m_nX;

  ImageType::RegionType region(start, size);

  ImageType::Pointer image = ImageType::New();
  image->SetRegions(region);
  image->Allocate();
  image->FillBuffer(0);
  uchar *iptr = (uchar*)image->GetBufferPointer();
  memcpy(iptr, opacityVol, m_nX*m_nY*m_nZ);

  typedef itk::Image< float, 3 > OutputImageType;
  typedef itk::SignedMaurerDistanceMapImageFilter<ImageType, OutputImageType> DistanceMapFilter;
  DistanceMapFilter::Pointer filter = DistanceMapFilter::New();
  filter->SetInput( image );
  filter->SetSquaredDistance(0);
  filter->SetUseImageSpacing(0);
  filter->SetInsideIsPositive(1);
  filter->Update();

  QFile fp;
  fp.setFileName(flnm);
  fp.open(QFile::WriteOnly);
  uchar vt = 8;
  fp.write((char*)&vt, 1);
  fp.write((char*)&m_nX, 4);
  fp.write((char*)&m_nY, 4);
  fp.write((char*)&m_nZ, 4);
  OutputImageType *dimg = filter->GetOutput();
  char *tdata = (char*)(dimg->GetBufferPointer());
  fp.write(tdata, 4*m_nX*m_nY*m_nZ);
  fp.close();

  m_meshLog->moveCursor(QTextCursor::End);
  m_meshLog->insertPlainText("Signed Distance Map data saved in "+flnm);

  QMessageBox::information(0, "", QString("Signed Distance Map saved in "+flnm));
}
void
MeshGenerator::generateMesh(int nSlabs,
			    QStringList volumeFiles,
			    QString flnm,
			    int depth,
			    QGradientStops vstops,
			    int fillValue,
			    bool checkForMore,
			    bool lookInside,
			    Vec voxelScaling,
			    QList<Vec> clipPos,
			    QList<Vec> clipNormal,
			    QList<CropObject> crops,
			    QList<PathObject> paths,
			    uchar *lut,
			    int chan,
			    bool avgColor)
{
  bool saveIntermediate = false;

  int bpv = 1;
  if (m_voxelType > 0) bpv = 2;
  int nbytes = bpv*m_nY*m_nZ;

//  if (nSlabs > 1)
//    {
//      QStringList sl;
//      sl << "No";
//      sl << "Yes";
//      bool ok;
//      QString okstr = QInputDialog::getItem(0, "Save slab files",
//		      "Save slab files in .ply format.\nFiles will not be collated together to create a unified mesh for the whole sample.",
//					    sl, 0, false, &ok);
//      if (ok && okstr == "Yes")
//	saveIntermediate = true;
//    }

  QGradientStops lutstops;
  for(int i=0; i<255; i++)
    {
      QColor col(lut[4*i+0], lut[4*i+1], lut[4*i+2], lut[4*i+3]);
      lutstops << QGradientStop((float)i/(float)255.0f, col);
    }
  
  bool trim = (qRound(m_dataSize.x) < m_height ||
	       qRound(m_dataSize.y) < m_width ||
	       qRound(m_dataSize.z) < m_depth);
  bool clipPresent = (clipPos.count() > 0);

  m_cropPresent = false;
  m_tearPresent = false;
  m_blendPresent = false;
  for(int ci=0; ci<m_crops.count(); ci++)
    {
      if (crops[ci].cropType() < CropObject::Tear_Tear)
	m_cropPresent = true;
      else if (crops[ci].cropType() < CropObject::View_Tear)
	m_tearPresent = true;
      else if (m_crops[ci].cropType() > CropObject::Displace_Displace &&
	       m_crops[ci].cropType() < CropObject::Glow_Ball)
	m_blendPresent = true;
    }

  m_pathCropPresent = false;
  m_pathBlendPresent = false;
  for (int i=0; i<m_paths.count(); i++)
    {
      if (m_paths[i].blend()) m_pathBlendPresent = true;
      if (m_paths[i].crop()) m_pathCropPresent = true;
    }

  int nextra = depth;
  int blockStep = m_nX/nSlabs;


  //-----------------------------
  int nvols = volumeFiles.count();      
  //-----------------------------

  for (int volnum=0; volnum < nvols; volnum++)
    {
      //if (nvols > 1)
	{
	  m_vfm->setBaseFilename(volumeFiles[volnum]);
	  uchar *vslice = m_vfm->getSlice(0);
	}

      m_meshLog->moveCursor(QTextCursor::End);
      m_meshLog->insertPlainText(QString("\nProcessing file %1 of %2 : %3\n").\
				 arg(volnum+1).arg(nvols).arg(m_vfm->fileName()));

      for (int nb=0; nb<nSlabs; nb++)
	{
	  m_meshLog->moveCursor(QTextCursor::End);
	  m_meshLog->insertPlainText(QString("  Processing slab %1 of %2\n").arg(nb+1).arg(nSlabs));
	  int d0 = nb*blockStep;
	  int d1 = qMin(m_nX-1, (nb+1)*blockStep);
	  int dlen = d1-d0+1;
	  
	  int d0z = d0 + qRound(m_dataMin.z);
	  int d1z = d1 + qRound(m_dataMin.z);
	  
	  uchar *extData;
	  if (m_voxelType == 0)
	    extData = new uchar[(dlen+2*nextra)*m_nY*m_nZ];
	  else
	    extData = new uchar[2*(dlen+2*nextra)*m_nY*m_nZ]; // ushort
	  
	  uchar *cropped = new uchar[nbytes];
	  uchar *tmp = new uchar[nbytes];
	  
	  int i0 = 0;
	  for(int i=d0z-nextra; i<=d1z+nextra; i++)
	    {
	      m_meshProgress->setValue((int)(100.0*(float)(i0/(float)(dlen+2*nextra))));
	      qApp->processEvents();
	      
	      int iv = qBound(0, i, m_depth-1);
	      uchar *vslice = m_vfm->getSlice(iv);
	      
	      memset(cropped, 0, nbytes);
	      
	      if (!trim)
		memcpy(tmp, vslice, nbytes);
	      else
		{
		  int wmin = qRound(m_dataMin.y);
		  int hmin = qRound(m_dataMin.x);
		  if (m_voxelType == 0)
		    {
		      for(int w=0; w<m_nY; w++)
			for(int h=0; h<m_nZ; h++)
			  tmp[w*m_nZ + h] = vslice[(wmin+w)*m_height + (hmin+h)];
		    }
		  else
		    {
		      for(int w=0; w<m_nY; w++)
			for(int h=0; h<m_nZ; h++)
			  ((ushort*)tmp)[w*m_nZ + h] = ((ushort*)vslice)[(wmin+w)*m_height + (hmin+h)];
		    }
		}
	      
	      
	      int jk = 0;
	      for(int j=0; j<m_nY; j++)
		for(int k=0; k<m_nZ; k++)
		  {
		    Vec po = Vec(m_dataMin.x+k, m_dataMin.y+j, iv);
		    bool ok = true;
		    
		    // we don't want to scale before pruning
		    // no mop pruning

		    po *= m_samplingLevel;
		    
		    if (ok && clipPresent)
		      ok = StaticFunctions::getClip(po, clipPos, clipNormal);
		    
		    if (ok && m_cropPresent)
		      ok = checkCrop(po);
		    
		    if (ok && m_pathCropPresent)
		      ok = checkPathCrop(po);
		    
		    if (ok && m_blendPresent)
		      {
			ushort v;
			if (m_voxelType == 0)
			  v = tmp[j*m_nZ + k];
			else
			  v = ((ushort*)tmp)[j*m_nZ + k];
			ok = checkBlend(po, v, lut);
		      }
		    
		    if (ok && m_pathBlendPresent)
		      {
			ushort v;
			if (m_voxelType == 0)
			  v = tmp[j*m_nZ + k];
			else
			  v = ((ushort*)tmp)[j*m_nZ + k];
			ok = checkPathBlend(po, v, lut);
		      }
		    
		    if (ok)
		      //cropped[jk] = mop; // nop mop pruning
		      cropped[jk] = 255;
		    else
		      cropped[jk] = 0;
		    
		    jk ++;
		  }
	      
	      if (m_voxelType == 0)
		{
		  for(int j=0; j<m_nY*m_nZ; j++)
		    {
		      if (cropped[j] == 0)
			tmp[j] = 0;
		    }
		}
	      else
		{
		  for(int j=0; j<m_nY*m_nZ; j++)
		    {
		      if (cropped[j] == 0)
			((ushort*)tmp)[j] = 0;
		    }
		}
	      
	      // tmp now clipped and contains raw data
	      memcpy(extData + bpv*i0*m_nY*m_nZ, tmp, nbytes);
	      
	      i0++;
	    }
	  delete [] tmp;
	  delete [] cropped;
	  m_meshProgress->setValue(100);
	  qApp->processEvents();
	  
	  //------------
	  if (m_tearPresent)
	    {
	      uchar *data0 = new uchar[(dlen+2*nextra)*m_nY*m_nZ];
	      
	      uchar *data1 = extData;
	      memcpy(data0, data1, (dlen+2*nextra)*m_nY*m_nZ);
	      applyTear(d0, d1, nextra,
			data0, data1, true);
	      
	      delete [] data0;
	    }
	  //------------
	  
	  
	  //--------------------------------
	  // ---- set border voxels to fillValue
	  if (fillValue >= 0)
	    {
	      uchar *v = extData;
	      
	      i0 = 0;
	      for(int i=d0z-nextra; i<=d1z+nextra; i++)
		{
		  int iv = qBound(0, i, m_depth-1);
		  int i0dx = i0*m_nY*m_nZ;
		  if (iv <= qRound(m_dataMin.z) || iv >= qRound(m_dataMax.z))
		    {
		      if (m_voxelType == 0)
			memset(v + i0dx, fillValue, m_nY*m_nZ);
		      else
			{
			  for(int fi=0; fi<m_nY*m_nZ; fi++)
			    ((ushort*)v)[i0*m_nY*m_nZ + fi] = fillValue;
			}
		    }
		  else
		    {
		      if (m_voxelType == 0)
			{
			  for(int j=0; j<m_nY; j++)
			    v[i0dx + j*m_nZ] = fillValue;
			  for(int j=0; j<m_nY; j++)
			    v[i0dx + j*m_nZ + m_nZ-1] = fillValue;
			  for(int k=0; k<m_nZ; k++)
			    v[i0dx + k] = fillValue;
			  for(int k=0; k<m_nZ; k++)
			    v[i0dx + (m_nY-1)*m_nZ + k] = fillValue;
			}
		      else
			{
			  for(int j=0; j<m_nY; j++)
			    ((ushort*)v)[i0dx + j*m_nZ] = fillValue;
			  for(int j=0; j<m_nY; j++)
			    ((ushort*)v)[i0dx + j*m_nZ + m_nZ-1] = fillValue;
			  for(int k=0; k<m_nZ; k++)
			    ((ushort*)v)[i0dx + k] = fillValue;
			  for(int k=0; k<m_nZ; k++)
			    ((ushort*)v)[i0dx + (m_nY-1)*m_nZ + k] = fillValue;
			}
		    }
		  i0++;
		}
	    }
	  //--------------------------------
	  
	  m_meshLog->moveCursor(QTextCursor::End);
	  m_meshLog->insertPlainText("  Generating Color ...\n");
	  
	  for(int ni=0; ni<m_nverts; ni++)
	    {
	      m_meshProgress->setValue((int)(100.0*(float)ni/(float)m_nverts));
	      qApp->processEvents();
	      
	      float v[3];
	      v[0] = m_vlist[ni]->x/voxelScaling.x/m_scaleModel;
	      v[1] = m_vlist[ni]->y/voxelScaling.y/m_scaleModel;
	      v[2] = m_vlist[ni]->z/voxelScaling.z/m_scaleModel;

	      if (v[2] > d0 && v[2] <= d1)
		{
//		  QMessageBox::information(0, "", QString("%1 %2 %3\n%4 %5 %6\n%7 %8"). \
//					   arg(v[0]).arg(v[1]).arg(v[2]). \
//					   arg(m_vlist[ni]->x).arg(m_vlist[ni]->y).arg(m_vlist[ni]->z). \
//					   arg(d0).arg(d1));
		  		  
		  uchar *volData = extData;
		  QColor col;
		  QVector3D pos, normal;
		  pos = QVector3D(v[0], v[1], v[2]-d0 + nextra);
		  normal = QVector3D(-m_vlist[ni]->nx,
				     -m_vlist[ni]->ny,
				     -m_vlist[ni]->nz);
		  col = getVRLutColor(volData,
				      dlen,
				      depth, nextra,
				      pos, normal,
				      lut,
				      lookInside,
				      QVector3D(v[0], v[1], v[2]));
		      
		  if (col.alphaF() > 0)
		    {
		      float r = col.red()/255.0f;
		      float g = col.green()/255.0f;
		      float b = col.blue()/255.0f;
		      float a = col.alphaF();

		      // tinge with lutcolor
		      QColor col0 = vstops[255*(float)(volnum+1)/(float)nvols].second;
		      float aa = col0.alphaF();
		      float tr = col0.red()/255.0f;
		      float tg = col0.green()/255.0f;
		      float tb = col0.blue()/255.0f;
		      
		      r = (1.0-aa)*r + aa*a*tr;
		      g = (1.0-aa)*g + aa*a*tg;
		      b = (1.0-aa)*b + aa*a*tb;

		      vcolor[3*ni+0] = (1-a)*vcolor[3*ni+0] + r;
		      vcolor[3*ni+1] = (1-a)*vcolor[3*ni+1] + g;
		      vcolor[3*ni+2] = (1-a)*vcolor[3*ni+2] + b;
		    }
		}
	    }
	  m_meshProgress->setValue(100);
	  
	  delete [] extData;
	} // loop over slabs

//      if (nvols > 1) // save intermediate files
//	{
//	  QString plyflnm = flnm;
//	  plyflnm.chop(3);
//	  plyflnm += QString("%1.ply").arg((int)volnum, (int)nvols/10+2, 10, QChar('0'));
//
//	  for(int ni=0; ni<m_nverts; ni++)
//	    {
//	      m_vlist[ni]->r = 255*vcolor[3*ni+0];
//	      m_vlist[ni]->g = 255*vcolor[3*ni+1];
//	      m_vlist[ni]->b = 255*vcolor[3*ni+2];
//	    }
//	  savePLY(plyflnm);
//	}

    }// loop over files

  for(int ni=0; ni<m_nverts; ni++)
    {
      m_vlist[ni]->r = 255*vcolor[3*ni+0];
      m_vlist[ni]->g = 255*vcolor[3*ni+1];
      m_vlist[ni]->b = 255*vcolor[3*ni+2];
    }

  savePLY(flnm);

  m_meshLog->moveCursor(QTextCursor::End);
  m_meshLog->insertPlainText("Mesh saved in "+flnm);

  QMessageBox::information(0, "", QString("Mesh saved in "+flnm));


}