<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkClipDataSet.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 1993-2002 Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkClipDataSet.h"

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkClipVolume.h"
#include "vtkFloatArray.h"
#include "vtkGenericCell.h"
#include "vtkImageData.h"
#include "vtkImplicitFunction.h"
#include "vtkIntArray.h"
#include "vtkMergePoints.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnstructuredGrid.h"

#include <math.h>

vtkCxxRevisionMacro(vtkClipDataSet, "1.26");
vtkStandardNewMacro(vtkClipDataSet);
vtkCxxSetObjectMacro(vtkClipDataSet,ClipFunction,vtkImplicitFunction);

//----------------------------------------------------------------------------
// Construct with user-specified implicit function; InsideOut turned off; value
// set to 0.0; and generate clip scalars turned off.
vtkClipDataSet::vtkClipDataSet(vtkImplicitFunction *cf)
{
  this->ClipFunction = cf;
  this->InsideOut = 0;
  this->Locator = NULL;
  this->Value = 0.0;
  this->GenerateClipScalars = 0;

  this->GenerateClippedOutput = 0;
  this->MergeTolerance = 0.01;

  this->vtkSource::SetNthOutput(1,vtkUnstructuredGrid::New());
  this->Outputs[1]->Delete();
  this->InputScalarsSelection = NULL;
}

//----------------------------------------------------------------------------
vtkClipDataSet::~vtkClipDataSet()
{
  if ( this->Locator )
    {
    this->Locator->UnRegister(this);
    this->Locator = NULL;
    }
  this->SetClipFunction(NULL);
  this->SetInputScalarsSelection(NULL);
}

//----------------------------------------------------------------------------
// Do not say we have two outputs unless we are generating the clipped output.
int vtkClipDataSet::GetNumberOfOutputs()
{
  if (this->GenerateClippedOutput)
    {
    return 2;
    }
  return 1;
}

//----------------------------------------------------------------------------
// Overload standard modified time function. If Clip functions is modified,
// then this object is modified as well.
unsigned long vtkClipDataSet::GetMTime()
{
  unsigned long mTime=this->vtkDataSetToUnstructuredGridFilter::GetMTime();
  unsigned long time;

  if ( this->ClipFunction != NULL )
    {
    time = this->ClipFunction->GetMTime();
    mTime = ( time > mTime ? time : mTime );
    }
  if ( this->Locator != NULL )
    {
    time = this->Locator->GetMTime();
    mTime = ( time > mTime ? time : mTime );
    }

  return mTime;
}

vtkUnstructuredGrid *vtkClipDataSet::GetClippedOutput()
{
  if (this->NumberOfOutputs < 2)
    {
    return NULL;
    }

  return (vtkUnstructuredGrid *)(this->Outputs[1]);
}

//----------------------------------------------------------------------------
//
// Clip through data generating surface.
//
void vtkClipDataSet::Execute()
{
  vtkDataSet *input = this->GetInput();
  vtkUnstructuredGrid *output = this->GetOutput();
  vtkUnstructuredGrid *clippedOutput = this->GetClippedOutput();
  if (input == NULL)
    {
    return;
    }
  vtkIdType numPts = input->GetNumberOfPoints();
  vtkIdType numCells = input->GetNumberOfCells();
  vtkPointData *inPD=input->GetPointData(), *outPD = output->GetPointData();
  vtkCellData *inCD=input->GetCellData();
  vtkCellData *outCD[2];
  vtkPoints *newPoints;
  vtkFloatArray *cellScalars;
  vtkDataArray *clipScalars;
  vtkPoints *cellPts;
  vtkIdList *cellIds;
  float s;
  vtkIdType npts;
  vtkIdType *pts;
  int cellType = 0;
  vtkIdType i;
  int j;
  vtkIdType estimatedSize;
  vtkUnsignedCharArray *types[2];
  vtkIntArray *locs[2];
  int numOutputs = 1;
  vtkIdType newCellId;

  vtkDebugMacro(<< "Clipping dataset");

  int inputObjectType = input->GetDataObjectType();

  // if we have volumes
  if (inputObjectType == VTK_STRUCTURED_POINTS ||
      inputObjectType == VTK_IMAGE_DATA)
    {
    int dimension;
    int *dims = vtkImageData::SafeDownCast(input)->GetDimensions();
    for (dimension=3, i=0; i<3; i++)
      {
      if ( dims[i] <= 1 )
        {
        dimension--;
        }
      }
    if ( dimension >= 3 )
      {
      this->ClipVolume();
      return;
      }
     }

  // Initialize self; create output objects
  //
  if ( numPts < 1 )
    {
    //vtkErrorMacro(<<"No data to clip");
    return;
    }

  if ( !this->ClipFunction && this->GenerateClipScalars )
    {
    vtkErrorMacro(<<"Cannot generate clip scalars if no clip function defined");
    return;
    }

  // allocate the output and associated helper classes
  estimatedSize = numCells;
  estimatedSize = estimatedSize / 1024 * 1024; //multiple of 1024
  if (estimatedSize < 1024)
    {
    estimatedSize = 1024;
    }
  cellScalars = vtkFloatArray::New();
  cellScalars->Allocate(VTK_CELL_SIZE);
  vtkCellArray *conn[2];
  conn[0] = vtkCellArray::New();
  conn[0]->Allocate(estimatedSize,estimatedSize/2);
  conn[0]->InitTraversal();
  types[0] = vtkUnsignedCharArray::New();
  types[0]->Allocate(estimatedSize,estimatedSize/2);
  locs[0] = vtkIntArray::New();
  locs[0]->Allocate(estimatedSize,estimatedSize/2);
  if ( this->GenerateClippedOutput )
    {
    numOutputs = 2;
    conn[1] = vtkCellArray::New();
    conn[1]->Allocate(estimatedSize,estimatedSize/2);
    conn[1]->InitTraversal();
    types[1] = vtkUnsignedCharArray::New();
    types[1]->Allocate(estimatedSize,estimatedSize/2);
    locs[1] = vtkIntArray::New();
    locs[1]->Allocate(estimatedSize,estimatedSize/2);
    }
  newPoints = vtkPoints::New();
  newPoints->Allocate(numPts,numPts/2);

  // locator used to merge potentially duplicate points
  if ( this->Locator == NULL )
    {
    this->CreateDefaultLocator();
    }
  this->Locator->InitPointInsertion (newPoints, input->GetBounds());

  // Determine whether we're clipping with input scalars or a clip function
  // and do necessary setup.
  if ( this->ClipFunction )
    {
    vtkFloatArray *tmpScalars = vtkFloatArray::New();
    tmpScalars->SetNumberOfTuples(numPts);
    tmpScalars->SetName("ClipDataSetScalars");
    inPD = vtkPointData::New();
    inPD->ShallowCopy(input->GetPointData());//copies original
    inPD->AddArray(tmpScalars);
    if ( this->GenerateClipScalars )
      {
      inPD->SetActiveScalars(tmpScalars->GetName());
      }
    for ( i=0; i < numPts; i++ )
      {
      s = this->ClipFunction->FunctionValue(input->GetPoint(i));
      tmpScalars->SetTuple(i,&s);
      }
    clipScalars = tmpScalars;
    }
  else //using input scalars
    {
    clipScalars = inPD->GetScalars(this->InputScalarsSelection);
    if ( !clipScalars )
      {
      vtkErrorMacro(<<"Cannot clip without clip function or input scalars");
      return;
      }
    }

  if ( !this->GenerateClipScalars &&
       !input->GetPointData()->GetScalars(this->InputScalarsSelection))
    {
    outPD->CopyScalarsOff();
    }
  else
    {
    outPD->CopyScalarsOn();
    }
  outPD->InterpolateAllocate(inPD,estimatedSize,estimatedSize/2);
  outCD[0] = output->GetCellData();
  outCD[0]->CopyAllocate(inCD,estimatedSize,estimatedSize/2);
  if ( this->GenerateClippedOutput )
    {
    outCD[1] = clippedOutput->GetCellData();
    outCD[1]->CopyAllocate(inCD,estimatedSize,estimatedSize/2);
    }

  //Process all cells and clip each in turn
  //
  int abort=0;
  vtkIdType updateTime = numCells/20 + 1;  // update roughly every 5%
  vtkGenericCell *cell = vtkGenericCell::New();
  int num[2]; num[0]=num[1]=0;
  int numNew[2]; numNew[0]=numNew[1]=0;
  for (vtkIdType cellId=0; cellId < numCells && !abort; cellId++)
    {
    if ( !(cellId % updateTime) )
      {
      this->UpdateProgress((float)cellId / numCells);
      abort = this->GetAbortExecute();
      }

    input->GetCell(cellId,cell);
    cellPts = cell->GetPoints();
    cellIds = cell->GetPointIds();
    npts = cellPts->GetNumberOfPoints();

    // evaluate implicit cutting function
    for ( i=0; i < npts; i++ )
      {
      s = clipScalars->GetComponent(cellIds->GetId(i), 0);
      cellScalars->InsertTuple(i, &s);
      }

    // perform the clipping
    cell->Clip(this->Value, cellScalars, this->Locator, conn[0],
               inPD, outPD, inCD, cellId, outCD[0], this->InsideOut);
    numNew[0] = conn[0]->GetNumberOfCells() - num[0];
    num[0] = conn[0]->GetNumberOfCells();

    if ( this->GenerateClippedOutput )
      {
      cell->Clip(this->Value, cellScalars, this->Locator, conn[1],
                 inPD, outPD, inCD, cellId, outCD[1], !this->InsideOut);
      numNew[1] = conn[1]->GetNumberOfCells() - num[1];
      num[1] = conn[1]->GetNumberOfCells();
      }

    for (i=0; i<numOutputs; i++) //for both outputs
      {
      for (j=0; j < numNew[i]; j++)
        {
        locs[i]->InsertNextValue(conn[i]->GetTraversalLocation());
        conn[i]->GetNextCell(npts,pts);

        //For each new cell added, got to set the type of the cell
        switch ( cell->GetCellDimension() )
          {
          case 0: //points are generated-------------------------------
            cellType = (npts > 1 ? VTK_POLY_VERTEX : VTK_VERTEX);
            break;

          case 1: //lines are generated----------------------------------
            cellType = (npts > 2 ? VTK_POLY_LINE : VTK_LINE);
            break;

          case 2: //polygons are generated------------------------------
            cellType = (npts == 3 ? VTK_TRIANGLE :
                        (npts == 4 ? VTK_QUAD : VTK_POLYGON));
            break;

          case 3: //tetrahedra are generated------------------------------
            cellType = VTK_TETRA;
            break;
          } //switch

        newCellId = types[i]->InsertNextValue(cellType);
        outCD[i]->CopyData(inCD, cellId, newCellId);
        } //for each new cell
      } //for both outputs
    } //for each cell

  cell->Delete();
  cellScalars->Delete();

  if ( this->ClipFunction )
    {
    clipScalars->Delete();
    inPD->Delete();
    }

  output->SetPoints(newPoints);
  output->SetCells(types[0], locs[0], conn[0]);
  conn[0]->Delete();
  types[0]->Delete();
  locs[0]->Delete();

  if ( this->GenerateClippedOutput )
    {
    clippedOutput->SetPoints(newPoints);
    clippedOutput->SetCells(types[1], locs[1], conn[1]);
    conn[1]->Delete();
    types[1]->Delete();
    locs[1]->Delete();
    }

  newPoints->Delete();
  this->Locator->Initialize();//release any extra memory
  output->Squeeze();
}

//----------------------------------------------------------------------------
// Specify a spatial locator for merging points. By default,
// an instance of vtkMergePoints is used.
void vtkClipDataSet::SetLocator(vtkPointLocator *locator)
{
  if ( this->Locator == locator)
    {
    return;
    }

  if ( this->Locator )
    {
    this->Locator->UnRegister(this);
    this->Locator = NULL;
    }

  if ( locator )
    {
    locator->Register(this);
    }

  this->Locator = locator;
  this->Modified();
}

//----------------------------------------------------------------------------
void vtkClipDataSet::CreateDefaultLocator()
{
  if ( this->Locator == NULL )
    {
    this->Locator = vtkMergePoints::New();
    this->Locator->Register(this);
    this->Locator->Delete();
    }
}

//----------------------------------------------------------------------------
void vtkClipDataSet::ClipVolume()
{
  vtkClipVolume *clipVolume = vtkClipVolume::New();
  clipVolume->SetInput((vtkImageData *)this->GetInput());
  clipVolume->SetValue(this->Value);
  clipVolume->SetInsideOut(this->InsideOut);
  clipVolume->SetClipFunction(this->ClipFunction);
  clipVolume->SetGenerateClipScalars(this->GenerateClipScalars);
  clipVolume->SetGenerateClippedOutput(this->GenerateClippedOutput);
  clipVolume->SetMergeTolerance(this->MergeTolerance);
  clipVolume->SetDebug(this->Debug);
  clipVolume->Update();
  vtkUnstructuredGrid *clipOutput = clipVolume->GetOutput();

  vtkUnstructuredGrid *output = this->GetOutput();
  output->CopyStructure(clipOutput);
  output->GetPointData()->ShallowCopy(clipOutput->GetPointData());
  output->GetCellData()->ShallowCopy(clipOutput->GetCellData());
  clipVolume->Delete();
}

//----------------------------------------------------------------------------
void vtkClipDataSet::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "Merge Tolerance: " << this->MergeTolerance << "\n";
  if ( this->ClipFunction )
    {
    os << indent << "Clip Function: " << this->ClipFunction << "\n";
    }
  else
    {
    os << indent << "Clip Function: (none)\n";
    }
  os << indent << "InsideOut: " << (this->InsideOut ? "On\n" : "Off\n");
  os << indent << "Value: " << this->Value << "\n";
  if ( this->Locator )
    {
    os << indent << "Locator: " << this->Locator << "\n";
    }
  else
    {
    os << indent << "Locator: (none)\n";
    }

  os << indent << "Generate Clip Scalars: "
     << (this->GenerateClipScalars ? "On\n" : "Off\n");

  os << indent << "Generate Clipped Output: "
     << (this->GenerateClippedOutput ? "On\n" : "Off\n");

  if (this->InputScalarsSelection)
    {
    os << indent << "InputScalarsSelection: "
       << this->InputScalarsSelection << endl;
    }
}
<commit_msg>ENH:Clipping is working faster and more robust<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkClipDataSet.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 1993-2002 Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkClipDataSet.h"

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkClipVolume.h"
#include "vtkFloatArray.h"
#include "vtkGenericCell.h"
#include "vtkImageData.h"
#include "vtkImplicitFunction.h"
#include "vtkIntArray.h"
#include "vtkMergePoints.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnstructuredGrid.h"

#include <math.h>

vtkCxxRevisionMacro(vtkClipDataSet, "1.27");
vtkStandardNewMacro(vtkClipDataSet);
vtkCxxSetObjectMacro(vtkClipDataSet,ClipFunction,vtkImplicitFunction);

//----------------------------------------------------------------------------
// Construct with user-specified implicit function; InsideOut turned off; value
// set to 0.0; and generate clip scalars turned off.
vtkClipDataSet::vtkClipDataSet(vtkImplicitFunction *cf)
{
  this->ClipFunction = cf;
  this->InsideOut = 0;
  this->Locator = NULL;
  this->Value = 0.0;
  this->GenerateClipScalars = 0;

  this->GenerateClippedOutput = 0;
  this->MergeTolerance = 0.01;

  this->vtkSource::SetNthOutput(1,vtkUnstructuredGrid::New());
  this->Outputs[1]->Delete();
  this->InputScalarsSelection = NULL;
}

//----------------------------------------------------------------------------
vtkClipDataSet::~vtkClipDataSet()
{
  if ( this->Locator )
    {
    this->Locator->UnRegister(this);
    this->Locator = NULL;
    }
  this->SetClipFunction(NULL);
  this->SetInputScalarsSelection(NULL);
}

//----------------------------------------------------------------------------
// Do not say we have two outputs unless we are generating the clipped output.
int vtkClipDataSet::GetNumberOfOutputs()
{
  if (this->GenerateClippedOutput)
    {
    return 2;
    }
  return 1;
}

//----------------------------------------------------------------------------
// Overload standard modified time function. If Clip functions is modified,
// then this object is modified as well.
unsigned long vtkClipDataSet::GetMTime()
{
  unsigned long mTime=this->vtkDataSetToUnstructuredGridFilter::GetMTime();
  unsigned long time;

  if ( this->ClipFunction != NULL )
    {
    time = this->ClipFunction->GetMTime();
    mTime = ( time > mTime ? time : mTime );
    }
  if ( this->Locator != NULL )
    {
    time = this->Locator->GetMTime();
    mTime = ( time > mTime ? time : mTime );
    }

  return mTime;
}

vtkUnstructuredGrid *vtkClipDataSet::GetClippedOutput()
{
  if (this->NumberOfOutputs < 2)
    {
    return NULL;
    }

  return (vtkUnstructuredGrid *)(this->Outputs[1]);
}

//----------------------------------------------------------------------------
//
// Clip through data generating surface.
//
void vtkClipDataSet::Execute()
{
  vtkDataSet *input = this->GetInput();
  vtkUnstructuredGrid *output = this->GetOutput();
  vtkUnstructuredGrid *clippedOutput = this->GetClippedOutput();
  if (input == NULL)
    {
    return;
    }
  vtkIdType numPts = input->GetNumberOfPoints();
  vtkIdType numCells = input->GetNumberOfCells();
  vtkPointData *inPD=input->GetPointData(), *outPD = output->GetPointData();
  vtkCellData *inCD=input->GetCellData();
  vtkCellData *outCD[2];
  vtkPoints *newPoints;
  vtkFloatArray *cellScalars;
  vtkDataArray *clipScalars;
  vtkPoints *cellPts;
  vtkIdList *cellIds;
  float s;
  vtkIdType npts;
  vtkIdType *pts;
  int cellType = 0;
  vtkIdType i;
  int j;
  vtkIdType estimatedSize;
  vtkUnsignedCharArray *types[2];
  vtkIntArray *locs[2];
  int numOutputs = 1;
  vtkIdType newCellId;

  vtkDebugMacro(<< "Clipping dataset");

  int inputObjectType = input->GetDataObjectType();

  // if we have volumes
  if (inputObjectType == VTK_STRUCTURED_POINTS ||
      inputObjectType == VTK_IMAGE_DATA)
    {
    int dimension;
    int *dims = vtkImageData::SafeDownCast(input)->GetDimensions();
    for (dimension=3, i=0; i<3; i++)
      {
      if ( dims[i] <= 1 )
        {
        dimension--;
        }
      }
    if ( dimension >= 3 )
      {
      this->ClipVolume();
      return;
      }
     }

  // Initialize self; create output objects
  //
  if ( numPts < 1 )
    {
    //vtkErrorMacro(<<"No data to clip");
    return;
    }

  if ( !this->ClipFunction && this->GenerateClipScalars )
    {
    vtkErrorMacro(<<"Cannot generate clip scalars if no clip function defined");
    return;
    }

  // allocate the output and associated helper classes
  estimatedSize = numCells;
  estimatedSize = estimatedSize / 1024 * 1024; //multiple of 1024
  if (estimatedSize < 1024)
    {
    estimatedSize = 1024;
    }
  cellScalars = vtkFloatArray::New();
  cellScalars->Allocate(VTK_CELL_SIZE);
  vtkCellArray *conn[2];
  conn[0] = vtkCellArray::New();
  conn[0]->Allocate(estimatedSize,estimatedSize/2);
  conn[0]->InitTraversal();
  types[0] = vtkUnsignedCharArray::New();
  types[0]->Allocate(estimatedSize,estimatedSize/2);
  locs[0] = vtkIntArray::New();
  locs[0]->Allocate(estimatedSize,estimatedSize/2);
  if ( this->GenerateClippedOutput )
    {
    numOutputs = 2;
    conn[1] = vtkCellArray::New();
    conn[1]->Allocate(estimatedSize,estimatedSize/2);
    conn[1]->InitTraversal();
    types[1] = vtkUnsignedCharArray::New();
    types[1]->Allocate(estimatedSize,estimatedSize/2);
    locs[1] = vtkIntArray::New();
    locs[1]->Allocate(estimatedSize,estimatedSize/2);
    }
  newPoints = vtkPoints::New();
  newPoints->Allocate(numPts,numPts/2);

  // locator used to merge potentially duplicate points
  if ( this->Locator == NULL )
    {
    this->CreateDefaultLocator();
    }
  this->Locator->InitPointInsertion (newPoints, input->GetBounds());

  // Determine whether we're clipping with input scalars or a clip function
  // and do necessary setup.
  if ( this->ClipFunction )
    {
    vtkFloatArray *tmpScalars = vtkFloatArray::New();
    tmpScalars->SetNumberOfTuples(numPts);
    tmpScalars->SetName("ClipDataSetScalars");
    inPD = vtkPointData::New();
    inPD->ShallowCopy(input->GetPointData());//copies original
    if ( this->GenerateClipScalars )
      {
      inPD->SetScalars(tmpScalars);
      }
    for ( i=0; i < numPts; i++ )
      {
      s = this->ClipFunction->FunctionValue(input->GetPoint(i));
      tmpScalars->SetTuple1(i,s);
      }
    clipScalars = tmpScalars;
    }
  else //using input scalars
    {
    clipScalars = inPD->GetScalars(this->InputScalarsSelection);
    if ( !clipScalars )
      {
      vtkErrorMacro(<<"Cannot clip without clip function or input scalars");
      return;
      }
    }

  if ( !this->GenerateClipScalars &&
       !input->GetPointData()->GetScalars(this->InputScalarsSelection))
    {
    outPD->CopyScalarsOff();
    }
  else
    {
    outPD->CopyScalarsOn();
    }
  outPD->InterpolateAllocate(inPD,estimatedSize,estimatedSize/2);
  outCD[0] = output->GetCellData();
  outCD[0]->CopyAllocate(inCD,estimatedSize,estimatedSize/2);
  if ( this->GenerateClippedOutput )
    {
    outCD[1] = clippedOutput->GetCellData();
    outCD[1]->CopyAllocate(inCD,estimatedSize,estimatedSize/2);
    }

  //Process all cells and clip each in turn
  //
  int abort=0;
  vtkIdType updateTime = numCells/20 + 1;  // update roughly every 5%
  vtkGenericCell *cell = vtkGenericCell::New();
  int num[2]; num[0]=num[1]=0;
  int numNew[2]; numNew[0]=numNew[1]=0;
  for (vtkIdType cellId=0; cellId < numCells && !abort; cellId++)
    {
    if ( !(cellId % updateTime) )
      {
      this->UpdateProgress((float)cellId / numCells);
      abort = this->GetAbortExecute();
      }

    input->GetCell(cellId,cell);
    cellPts = cell->GetPoints();
    cellIds = cell->GetPointIds();
    npts = cellPts->GetNumberOfPoints();

    // evaluate implicit cutting function
    for ( i=0; i < npts; i++ )
      {
      s = clipScalars->GetComponent(cellIds->GetId(i), 0);
      cellScalars->InsertTuple(i, &s);
      }

    // perform the clipping
    cell->Clip(this->Value, cellScalars, this->Locator, conn[0],
               inPD, outPD, inCD, cellId, outCD[0], this->InsideOut);
    numNew[0] = conn[0]->GetNumberOfCells() - num[0];
    num[0] = conn[0]->GetNumberOfCells();

    if ( this->GenerateClippedOutput )
      {
      cell->Clip(this->Value, cellScalars, this->Locator, conn[1],
                 inPD, outPD, inCD, cellId, outCD[1], !this->InsideOut);
      numNew[1] = conn[1]->GetNumberOfCells() - num[1];
      num[1] = conn[1]->GetNumberOfCells();
      }

    for (i=0; i<numOutputs; i++) //for both outputs
      {
      for (j=0; j < numNew[i]; j++)
        {
        locs[i]->InsertNextValue(conn[i]->GetTraversalLocation());
        conn[i]->GetNextCell(npts,pts);

        //For each new cell added, got to set the type of the cell
        switch ( cell->GetCellDimension() )
          {
          case 0: //points are generated-------------------------------
            cellType = (npts > 1 ? VTK_POLY_VERTEX : VTK_VERTEX);
            break;

          case 1: //lines are generated----------------------------------
            cellType = (npts > 2 ? VTK_POLY_LINE : VTK_LINE);
            break;

          case 2: //polygons are generated------------------------------
            cellType = (npts == 3 ? VTK_TRIANGLE :
                        (npts == 4 ? VTK_QUAD : VTK_POLYGON));
            break;

          case 3: //tetrahedra or wedges are generated------------------------------
            cellType = (npts == 4 ? VTK_TETRA : VTK_WEDGE);
            break;
          } //switch

        newCellId = types[i]->InsertNextValue(cellType);
        outCD[i]->CopyData(inCD, cellId, newCellId);
        } //for each new cell
      } //for both outputs
    } //for each cell

  cell->Delete();
  cellScalars->Delete();

  if ( this->ClipFunction )
    {
    clipScalars->Delete();
    inPD->Delete();
    }

  output->SetPoints(newPoints);
  output->SetCells(types[0], locs[0], conn[0]);
  conn[0]->Delete();
  types[0]->Delete();
  locs[0]->Delete();

  if ( this->GenerateClippedOutput )
    {
    clippedOutput->SetPoints(newPoints);
    clippedOutput->SetCells(types[1], locs[1], conn[1]);
    conn[1]->Delete();
    types[1]->Delete();
    locs[1]->Delete();
    }

  newPoints->Delete();
  this->Locator->Initialize();//release any extra memory
  output->Squeeze();
}

//----------------------------------------------------------------------------
// Specify a spatial locator for merging points. By default,
// an instance of vtkMergePoints is used.
void vtkClipDataSet::SetLocator(vtkPointLocator *locator)
{
  if ( this->Locator == locator)
    {
    return;
    }

  if ( this->Locator )
    {
    this->Locator->UnRegister(this);
    this->Locator = NULL;
    }

  if ( locator )
    {
    locator->Register(this);
    }

  this->Locator = locator;
  this->Modified();
}

//----------------------------------------------------------------------------
void vtkClipDataSet::CreateDefaultLocator()
{
  if ( this->Locator == NULL )
    {
    this->Locator = vtkMergePoints::New();
    this->Locator->Register(this);
    this->Locator->Delete();
    }
}

//----------------------------------------------------------------------------
void vtkClipDataSet::ClipVolume()
{
  vtkClipVolume *clipVolume = vtkClipVolume::New();
  clipVolume->SetInput((vtkImageData *)this->GetInput());
  clipVolume->SetValue(this->Value);
  clipVolume->SetInsideOut(this->InsideOut);
  clipVolume->SetClipFunction(this->ClipFunction);
  clipVolume->SetGenerateClipScalars(this->GenerateClipScalars);
  clipVolume->SetGenerateClippedOutput(this->GenerateClippedOutput);
  clipVolume->SetMergeTolerance(this->MergeTolerance);
  clipVolume->SetDebug(this->Debug);
  clipVolume->Update();
  vtkUnstructuredGrid *clipOutput = clipVolume->GetOutput();

  vtkUnstructuredGrid *output = this->GetOutput();
  output->CopyStructure(clipOutput);
  output->GetPointData()->ShallowCopy(clipOutput->GetPointData());
  output->GetCellData()->ShallowCopy(clipOutput->GetCellData());
  clipVolume->Delete();
}

//----------------------------------------------------------------------------
void vtkClipDataSet::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "Merge Tolerance: " << this->MergeTolerance << "\n";
  if ( this->ClipFunction )
    {
    os << indent << "Clip Function: " << this->ClipFunction << "\n";
    }
  else
    {
    os << indent << "Clip Function: (none)\n";
    }
  os << indent << "InsideOut: " << (this->InsideOut ? "On\n" : "Off\n");
  os << indent << "Value: " << this->Value << "\n";
  if ( this->Locator )
    {
    os << indent << "Locator: " << this->Locator << "\n";
    }
  else
    {
    os << indent << "Locator: (none)\n";
    }

  os << indent << "Generate Clip Scalars: "
     << (this->GenerateClipScalars ? "On\n" : "Off\n");

  os << indent << "Generate Clipped Output: "
     << (this->GenerateClippedOutput ? "On\n" : "Off\n");

  if (this->InputScalarsSelection)
    {
    os << indent << "InputScalarsSelection: "
       << this->InputScalarsSelection << endl;
    }
}
<|endoftext|>


<commit_before>//
//  Landscape.cpp
//
//  Created by Soso Limited on 5/26/15.
//
//

#include "Landscape.h"

#include "cinder/Log.h"
#include "Constants.h"

using namespace soso;
using namespace cinder;

namespace {

struct alignas(16) Vertex {
	vec3	position;
	vec3	normal;
	vec2	tex_coord;
	vec2	color_tex_coord;
	float deform_scaling;
	float frame_offset;
};

const auto kVertexLayout = ([] {
		auto layout = geom::BufferLayout();
		layout.append( geom::Attrib::POSITION, 3, sizeof(Vertex), offsetof(Vertex, position) );
		layout.append( geom::Attrib::NORMAL, 3, sizeof(Vertex), offsetof(Vertex, normal) );
		layout.append( geom::Attrib::TEX_COORD_0, 2, sizeof(Vertex), offsetof(Vertex, tex_coord) );
		layout.append( geom::Attrib::TEX_COORD_1, 2, sizeof(Vertex), offsetof(Vertex, color_tex_coord) );
		layout.append( geom::Attrib::CUSTOM_0, 1, sizeof(Vertex), offsetof(Vertex, deform_scaling) );
		layout.append( geom::Attrib::CUSTOM_1, 1, sizeof(Vertex), offsetof(Vertex, frame_offset) );
		return layout;
	} ());

const auto kVertexMapping = ([] {
		return gl::Batch::AttributeMapping{ { geom::Attrib::CUSTOM_0, "DeformScaling" }, { geom::Attrib::CUSTOM_1, "FrameOffset" } };
	} ());

// Load a shader and handle exceptions. Return nullptr on failure.
gl::GlslProgRef loadShader( const fs::path &iVertex, const fs::path &iFragment )
{
	try {
		auto shader_format = gl::GlslProg::Format()
			.vertex( app::loadAsset( iVertex ) )
			.fragment( app::loadAsset( iFragment ) );

		return gl::GlslProg::create( shader_format );
	}
	catch( ci::Exception &exc ) {
		CI_LOG_E( "Error loading shader: " << exc.what() );
	}

	return nullptr;
}

///
/// Create a quadrilateral with clockwise vertices abcd.
/// Maps to frame at time and a slice of the video frame.
///
void addQuad( std::vector<Vertex> &vertices, const vec3 &a, const vec3 &b, const vec3 &c, const vec3 &d, float time, const Rectf &slice )
{
	auto normal = vec3( 0, 1, 0 );
	auto deform_scaling = 0.0f;

	vertices.insert( vertices.end(), {
		Vertex{ a, normal, slice.getUpperLeft(), slice.getUpperLeft(), deform_scaling, time },
		Vertex{ b, normal, slice.getUpperRight(), slice.getUpperRight(), deform_scaling, time },
		Vertex{ c, normal, slice.getLowerRight(), slice.getLowerRight(), deform_scaling, time },

		Vertex{ a, normal, slice.getUpperLeft(), slice.getUpperLeft(), deform_scaling, time },
		Vertex{ c, normal, slice.getLowerRight(), slice.getLowerRight(), deform_scaling, time },
		Vertex{ d, normal, slice.getLowerLeft(), slice.getLowerLeft(), deform_scaling, time }
	} );

}

void addRectangle( std::vector<Vertex> &vertices, const ci::vec3 &iCenter, float iWidth, float iHeight, float iFrameOffset )
{
	auto right = vec3( iWidth / 2.0f, 0, 0 );
	auto left = - right;
	auto down = vec3( 0, 0, iHeight / 2.0f );
	auto up = - down;

	auto ul = iCenter + (left + up);
	auto ur = iCenter + (right + up);
	auto br = iCenter + (right + down);
	auto bl = iCenter + (left + down);
	addQuad( vertices, ul, ur, br, bl, iFrameOffset, Rectf( 0.25, 0.4, 0.75, 0.6 ) );
}

void addStrip( std::vector<Vertex> &vertices, const vec3 &base, const vec3 &ray, const vec3 &normal, float inner_width, float outer_width )
{
	const auto inner_radius = 1.0f;
	const auto outer_radius = 64.0f;

	auto total_things = 64;
	auto inner_things = 8;
	auto outer_things = total_things - inner_things;
	auto perp = glm::rotate<float>( ray, Tau * 0.25f, normal );

	// Add full frames
	for (auto i = 0; i < inner_things; i += 1)
	{
		auto d1 = (i + 0.0f) / total_things;
		auto d2 = (i + 1.0f) / total_things;
		auto r1 = mix( inner_radius, outer_radius, d1 );
		auto r2 = mix( inner_radius, outer_radius, d2 );
		auto time = (i + 0.0f) / total_things;
		auto w1 = mix( inner_width, outer_width, d1 );
		auto w2 = mix( inner_width, outer_width, d2 );

		auto p1 = perp * w1 / 2.0f;
		auto p2 = perp * w2 / 2.0f;

		auto a = base + ray * r2 - p2;
		auto b = base + ray * r2 + p2;
		auto c = base + ray * r1 + p1;
		auto d = base + ray * r1 - p1;

		addQuad(vertices, a, b, c, d, time, Rectf(0, 1, 1, 0));
	}

	for (auto i = inner_things; i < total_things; i += 1)
	{
		auto outer_d1 = (i + 0.0f) / total_things;
		auto outer_d2 = (i + 1.0f) / total_things;
		auto slices = 48;

		for( auto s = 0; s < slices; s += 1 )
		{
			auto t1 = (s + 0.0f) / slices;
			auto t2 = (s + 1.0f) / slices;
			auto d1 = mix( outer_d1, outer_d2, t1 );
			auto d2 = mix( outer_d1, outer_d2, t2 );

			auto r1 = mix( inner_radius, outer_radius, d1 );
			auto r2 = mix( inner_radius, outer_radius, d2 );

			auto time = (i + 0.0f) / total_things + (s + 0.0f) / slices;
			auto w1 = mix( inner_width, outer_width, d1 );
			auto w2 = mix( inner_width, outer_width, d2 );

			auto p1 = perp * w1 / 2.0f;
			auto p2 = perp * w2 / 2.0f;

			auto a = base + ray * r2 - p2;
			auto b = base + ray * r2 + p2;
			auto c = base + ray * r1 + p1;
			auto d = base + ray * r1 - p1;

			addQuad(vertices, a, b, c, d, time, Rectf(0, t2, 1, t1));
		}
	}

	// Progressively deteriorating frames
}

/// Add a ring of geometry containing a given number of time bands (slitscanning effect) and repeats around the donut.
void addRing( std::vector<Vertex> &vertices, const vec3 &center, const vec3 &normal, float inner_radius, float outer_radius, float time_offset, int time_bands, int repeats )
{
	auto segments = 32;

	// Generate cartesian position.
	auto calc_pos = [=] (float distance, int s) {
		auto radius = mix( inner_radius, outer_radius, distance );
		auto t = (float) s / segments;
		auto x = cos( t * Tau ) * radius;
		auto y = sin( t * Tau ) * radius;
		return vec3( x, -4.0f, y );
	};

	// Generate texture coordinate mirrored at halfway point.
	auto calc_tc = [=] (int r, int s) {
		auto t = (float) s / segments;
		auto tc = vec2(0);
		// insetting the texture coordinates minimizes edge color flashing.
		tc.y = mix( 0.05f, 0.95f, std::abs( t - 0.5f ) * 2.0f );
		tc.x = lmap<float>( r, 0, 1, 0.9125f, 0.0875f );
		return tc;
	};

	// Add a vertex to texture (color_tc parameter allows us to do flat shading in ES2)
	auto add_vert = [=,&vertices] (int r, int s, const vec2 &color_tc) {
		auto deform_scaling = 0.0f;
		auto pos = calc_pos(r, s);
		auto tc = calc_tc(r, s);
		vertices.push_back( Vertex{ pos, normal, tc, color_tc, deform_scaling, time_offset } );
	};

	// Create triangles for flat shading
	for( auto s = 0; s < segments; s += 1 )
	{
		auto color_tc = calc_tc( 0.0f, s );
		add_vert( 0.0f, s, color_tc );
		add_vert( 0.0f, s + 1, color_tc );
		add_vert( 1.0f, s + 1, color_tc );

		add_vert( 0.0f, s, color_tc );
		add_vert( 1.0f, s, color_tc );
		add_vert( 1.0f, s + 1, color_tc );
	}
}

} // namespace

void Landscape::setup()
{
	auto shader = loadShader( "landscape.vs", "landscape.fs" );

	std::vector<Vertex> vertices;

	auto normal = vec3( 0, 1, 0 );

	addRing( vertices, vec3( 0, -3, 0 ), normal, 1.0f, 2.0f, 0.0f, 1, 2 );
//	addRing( vertices, vec3( 0, -3, 0 ), normal, 2.5f, 3.5f, 1.0f, 8, 4 );

	auto vbo = gl::Vbo::create( GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW );
	auto mesh = gl::VboMesh::create( vertices.size(), GL_TRIANGLES, {{ kVertexLayout, vbo }} );
	batch = gl::Batch::create( mesh, shader, kVertexMapping );
}

void Landscape::setTextureUnits( uint8_t iClearUnit, uint8_t iBlurredUnit )
{
	auto &shader = batch->getGlslProg();
	shader->uniform( "uClearTexture", iClearUnit );
	shader->uniform( "uBlurredTexture", iBlurredUnit );
}

void Landscape::draw( float iFrameOffset )
{
	auto &shader = batch->getGlslProg();
	shader->uniform( "uCurrentFrame", iFrameOffset );

	batch->draw();
}
<commit_msg>Use provoking vertex for calculating time offset.<commit_after>//
//  Landscape.cpp
//
//  Created by Soso Limited on 5/26/15.
//
//

#include "Landscape.h"

#include "cinder/Log.h"
#include "Constants.h"

using namespace soso;
using namespace cinder;

namespace {

struct alignas(16) Vertex {
	vec3	position;
	vec3	normal;
	vec2	tex_coord;
	vec2	color_tex_coord;
	float deform_scaling;
	float frame_offset;
};

const auto kVertexLayout = ([] {
		auto layout = geom::BufferLayout();
		layout.append( geom::Attrib::POSITION, 3, sizeof(Vertex), offsetof(Vertex, position) );
		layout.append( geom::Attrib::NORMAL, 3, sizeof(Vertex), offsetof(Vertex, normal) );
		layout.append( geom::Attrib::TEX_COORD_0, 2, sizeof(Vertex), offsetof(Vertex, tex_coord) );
		layout.append( geom::Attrib::TEX_COORD_1, 2, sizeof(Vertex), offsetof(Vertex, color_tex_coord) );
		layout.append( geom::Attrib::CUSTOM_0, 1, sizeof(Vertex), offsetof(Vertex, deform_scaling) );
		layout.append( geom::Attrib::CUSTOM_1, 1, sizeof(Vertex), offsetof(Vertex, frame_offset) );
		return layout;
	} ());

const auto kVertexMapping = ([] {
		return gl::Batch::AttributeMapping{ { geom::Attrib::CUSTOM_0, "DeformScaling" }, { geom::Attrib::CUSTOM_1, "FrameOffset" } };
	} ());

// Load a shader and handle exceptions. Return nullptr on failure.
gl::GlslProgRef loadShader( const fs::path &iVertex, const fs::path &iFragment )
{
	try {
		auto shader_format = gl::GlslProg::Format()
			.vertex( app::loadAsset( iVertex ) )
			.fragment( app::loadAsset( iFragment ) );

		return gl::GlslProg::create( shader_format );
	}
	catch( ci::Exception &exc ) {
		CI_LOG_E( "Error loading shader: " << exc.what() );
	}

	return nullptr;
}

///
/// Create a quadrilateral with clockwise vertices abcd.
/// Maps to frame at time and a slice of the video frame.
///
void addQuad( std::vector<Vertex> &vertices, const vec3 &a, const vec3 &b, const vec3 &c, const vec3 &d, float time, const Rectf &slice )
{
	auto normal = vec3( 0, 1, 0 );
	auto deform_scaling = 0.0f;

	vertices.insert( vertices.end(), {
		Vertex{ a, normal, slice.getUpperLeft(), slice.getUpperLeft(), deform_scaling, time },
		Vertex{ b, normal, slice.getUpperRight(), slice.getUpperRight(), deform_scaling, time },
		Vertex{ c, normal, slice.getLowerRight(), slice.getLowerRight(), deform_scaling, time },

		Vertex{ a, normal, slice.getUpperLeft(), slice.getUpperLeft(), deform_scaling, time },
		Vertex{ c, normal, slice.getLowerRight(), slice.getLowerRight(), deform_scaling, time },
		Vertex{ d, normal, slice.getLowerLeft(), slice.getLowerLeft(), deform_scaling, time }
	} );

}

void addRectangle( std::vector<Vertex> &vertices, const ci::vec3 &iCenter, float iWidth, float iHeight, float iFrameOffset )
{
	auto right = vec3( iWidth / 2.0f, 0, 0 );
	auto left = - right;
	auto down = vec3( 0, 0, iHeight / 2.0f );
	auto up = - down;

	auto ul = iCenter + (left + up);
	auto ur = iCenter + (right + up);
	auto br = iCenter + (right + down);
	auto bl = iCenter + (left + down);
	addQuad( vertices, ul, ur, br, bl, iFrameOffset, Rectf( 0.25, 0.4, 0.75, 0.6 ) );
}

void addStrip( std::vector<Vertex> &vertices, const vec3 &base, const vec3 &ray, const vec3 &normal, float inner_width, float outer_width )
{
	const auto inner_radius = 1.0f;
	const auto outer_radius = 64.0f;

	auto total_things = 64;
	auto inner_things = 8;
	auto outer_things = total_things - inner_things;
	auto perp = glm::rotate<float>( ray, Tau * 0.25f, normal );

	// Add full frames
	for (auto i = 0; i < inner_things; i += 1)
	{
		auto d1 = (i + 0.0f) / total_things;
		auto d2 = (i + 1.0f) / total_things;
		auto r1 = mix( inner_radius, outer_radius, d1 );
		auto r2 = mix( inner_radius, outer_radius, d2 );
		auto time = (i + 0.0f) / total_things;
		auto w1 = mix( inner_width, outer_width, d1 );
		auto w2 = mix( inner_width, outer_width, d2 );

		auto p1 = perp * w1 / 2.0f;
		auto p2 = perp * w2 / 2.0f;

		auto a = base + ray * r2 - p2;
		auto b = base + ray * r2 + p2;
		auto c = base + ray * r1 + p1;
		auto d = base + ray * r1 - p1;

		addQuad(vertices, a, b, c, d, time, Rectf(0, 1, 1, 0));
	}

	for (auto i = inner_things; i < total_things; i += 1)
	{
		auto outer_d1 = (i + 0.0f) / total_things;
		auto outer_d2 = (i + 1.0f) / total_things;
		auto slices = 48;

		for( auto s = 0; s < slices; s += 1 )
		{
			auto t1 = (s + 0.0f) / slices;
			auto t2 = (s + 1.0f) / slices;
			auto d1 = mix( outer_d1, outer_d2, t1 );
			auto d2 = mix( outer_d1, outer_d2, t2 );

			auto r1 = mix( inner_radius, outer_radius, d1 );
			auto r2 = mix( inner_radius, outer_radius, d2 );

			auto time = (i + 0.0f) / total_things + (s + 0.0f) / slices;
			auto w1 = mix( inner_width, outer_width, d1 );
			auto w2 = mix( inner_width, outer_width, d2 );

			auto p1 = perp * w1 / 2.0f;
			auto p2 = perp * w2 / 2.0f;

			auto a = base + ray * r2 - p2;
			auto b = base + ray * r2 + p2;
			auto c = base + ray * r1 + p1;
			auto d = base + ray * r1 - p1;

			addQuad(vertices, a, b, c, d, time, Rectf(0, t2, 1, t1));
		}
	}

	// Progressively deteriorating frames
}

/// Add a ring of geometry containing a given number of time bands (slitscanning effect) and repeats around the donut.
void addRing( std::vector<Vertex> &vertices, const vec3 &center, const vec3 &normal, float inner_radius, float outer_radius, float time_offset, int time_bands, int repeats )
{
	auto rings = time_bands;
	auto segments = 32;

	// Generate cartesian position.
	auto calc_pos = [=] (int r, int s) {
		auto distance = lmap<float>( r, 0, rings, 0.0f, 1.0f );
		auto radius = mix( inner_radius, outer_radius, distance );
		auto t = (float) s / segments;
		auto x = cos( t * Tau ) * radius;
		auto y = sin( t * Tau ) * radius;
		return vec3( x, -4.0f, y );
	};

	// Generate texture coordinate mirrored at halfway point.
	auto calc_tc = [=] (int r, int s) {
		auto t = (float) s / segments;
		auto tc = vec2(0);
		// insetting the texture coordinates minimizes edge color flashing.
		tc.y = mix( 0.05f, 0.95f, std::abs( t - 0.5f ) * 2.0f );
		tc.x = lmap<float>( r, 0, rings, 0.9125f, 0.0875f );
		return tc;
	};

	// Add a vertex to texture (color_tc parameter allows us to do flat shading in ES2)
	auto add_vert = [=,&vertices] (int r, int s, const ivec2 &provoking) {
		auto deform_scaling = 0.0f;
		auto pos = calc_pos(r, s);
		auto tc = calc_tc(r, s);
		auto color_tc = calc_tc( provoking.x, provoking.y );
		auto time = time_offset; // + lmap<float>( r, 0, rings, 0.0f, 1.0f / 64.0f );
		if (time_bands > 1) {
			time += lmap<float>( provoking.x, 0.0f, rings, 0.0f, time_bands );
		}
		vertices.push_back( Vertex{ pos, normal, tc, color_tc, deform_scaling, time } );
	};

	// Create triangles for flat shading
	for( auto r = 0; r < rings; r += 1 )
	{
		for( auto s = 0; s < segments; s += 1 )
		{
			auto provoking = ivec2(r, s);
			add_vert( r, s, provoking );
			add_vert( r, s + 1, provoking );
			add_vert( r + 1, s + 1, provoking );

			add_vert( r, s, provoking );
			add_vert( r + 1, s, provoking );
			add_vert( r + 1, s + 1, provoking );
		}
	}
}

} // namespace

void Landscape::setup()
{
	auto shader = loadShader( "landscape.vs", "landscape.fs" );

	std::vector<Vertex> vertices;

	auto normal = vec3( 0, 1, 0 );

	addRing( vertices, vec3( 0, -3, 0 ), normal, 1.0f, 2.0f, 0.0f, 1, 2 );
	addRing( vertices, vec3( 0, -3, 0 ), normal, 2.5f, 3.5f, 16.0f, 8, 4 );

	auto vbo = gl::Vbo::create( GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW );
	auto mesh = gl::VboMesh::create( vertices.size(), GL_TRIANGLES, {{ kVertexLayout, vbo }} );
	batch = gl::Batch::create( mesh, shader, kVertexMapping );
}

void Landscape::setTextureUnits( uint8_t iClearUnit, uint8_t iBlurredUnit )
{
	auto &shader = batch->getGlslProg();
	shader->uniform( "uClearTexture", iClearUnit );
	shader->uniform( "uBlurredTexture", iBlurredUnit );
}

void Landscape::draw( float iFrameOffset )
{
	auto &shader = batch->getGlslProg();
	shader->uniform( "uCurrentFrame", iFrameOffset );

	batch->draw();
}
<|endoftext|>


<commit_before>/*=========================================================================

Library:   TubeTK

Copyright 2010 Kitware Inc. 28 Corporate Drive,
Clifton Park, NY, 12065, USA.

All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=========================================================================*/
#include "ImageViewerConfigure.h"
#include "ImageViewerCLP.h"
//Qt includes
#include <QApplication>
#include <QDebug>
#include <QFileInfo>
#include <QKeyEvent>

#include <itkMedianImageFilter.h>
#include <itkConnectedThresholdImageFilter.h>
#include <itkExtractImageFilter.h>
#include <itkInvertIntensityImageFilter.h>
#include <itkSignedMaurerDistanceMapImageFilter.h>
#include <itkBinaryErodeImageFilter.h>
#include <itkBinaryDilateImageFilter.h>
#include <itkBinaryBallStructuringElement.h>
#include <itkMinimumMaximumImageCalculator.h>
#include <itkNeighborhoodIterator.h>
#include <itkStatisticsImageFilter.h>

//QtImageViewer includes
#include "QtGlSliceView.h"
#include "QtImageViewer.h"

int execImageViewer(int argc, char* argv[])
{
  QApplication myApp( argc, argv );

  QtImageViewer viewer( 0 );
  viewer.setWindowTitle("ImageViewer");
  viewer.loadInputImage();
  viewer.show();

  int execReturn;
  try
    {
    execReturn = myApp.exec();
    }
  catch (itk::ExceptionObject & e)
    {
    std::cerr << "Exception during GUI execution" << std::endl;
    std::cerr << e << std::endl;
    return EXIT_FAILURE;
    }
  return execReturn;
}

double IMAGE_MIN = 0;
double IMAGE_MAX = 1;

void myKeyCallback(QKeyEvent *event, void *d)
{
  if( event->key() == Qt::Key_Apostrophe )
    {
    QtGlSliceView * sv = (QtGlSliceView *)(d);

    typedef itk::Image< double, 3 >        ImageType;
    ImageType::Pointer img = sv->inputImage();

    typedef itk::MedianImageFilter< ImageType, ImageType >
                                           FilterType;
    FilterType::Pointer filter = FilterType::New();
    filter->SetInput( img );
    filter->SetRadius( 1 );
    filter->Update();
    sv->setInputImage( filter->GetOutput() );
    sv->update();
    }
}

void myMouseCallback(double x, double y, double z, double v, void *d)
{
  static itk::Index<3> PRESS_SEED;
  static double PRESS_SEED_V = 0;

  typedef itk::Image< double, 3 >        ImageType;
  typedef itk::Image< unsigned char, 3 > OverlayType;

  typedef itk::ConnectedThresholdImageFilter< ImageType, OverlayType >
                                         ConnCompFilterType;
  typedef itk::BinaryBallStructuringElement< unsigned char, 3 >
                                         StructuringElementType;
  typedef itk::BinaryErodeImageFilter< OverlayType, OverlayType,
                                         StructuringElementType >
                                         ErodeFilterType;
  typedef itk::BinaryDilateImageFilter< OverlayType, OverlayType,
                                         StructuringElementType >
                                         DilateFilterType;
  typedef itk::ExtractImageFilter< OverlayType, OverlayType >
                                         ExtractFilterType;
  typedef itk::InvertIntensityImageFilter< OverlayType, OverlayType >
                                         InvertFilterType;
  typedef itk::SignedMaurerDistanceMapImageFilter< OverlayType, ImageType >
                                         DistanceFilterType;
  typedef itk::MinimumMaximumImageCalculator< ImageType >
                                         MinMaxFilterType;

  QtGlSliceView * sv = (QtGlSliceView *)(d);

  if( sv->clickMode() == CM_CUSTOM )
    {
    if( sv->selectMovement() == SM_MOVE || sv->selectMovement() == SM_RELEASE )
      {
      itk::Index<3> seed;
      seed[0] = int( x );
      seed[1] = int( y );
      seed[2] = int( z );

      ImageType::Pointer img = sv->inputImage();

      double seedDistance = 0;
      for(unsigned int d=0; d<3; ++d)
        {
        double tf = seed[d] - PRESS_SEED[d];
        seedDistance += tf * tf;
        }
      seedDistance = std::sqrt(seedDistance);

      itk::NeighborhoodIterator<ImageType>::RadiusType seedRadius;
      for(unsigned int d=0; d<3; ++d)
        {
        seedRadius[d] = seedDistance/2;
        int limit = img->GetLargestPossibleRegion().GetSize()[d]/2-1;
        if( limit < 0 )
          {
          limit = 0;
          }
        if( seedRadius[d] > limit )
          {
          seedRadius[d] = limit;
          }
        }
      ImageType::IndexType indx = PRESS_SEED;

      double threshMin = PRESS_SEED_V;
      double threshMax = PRESS_SEED_V;
      itk::NeighborhoodIterator<ImageType> it( seedRadius, img,
        img->GetLargestPossibleRegion() );
      it.SetLocation( indx );
      for( unsigned int i=0; i<it.Size(); ++i )
        {
        double tf = it.GetPixel(i);
        if( tf < threshMin )
          {
          threshMin = tf;
          }
        else if( tf > threshMax )
          {
          threshMax = tf;
          }
        }

      double v = img->GetPixel(seed);
      if( v < threshMin )
        {
        threshMin = v;
        }
      else if( v > threshMax )
        {
        threshMax = v;
        }

      ConnCompFilterType::Pointer ccFilter = ConnCompFilterType::New();
      ccFilter->SetInput( sv->inputImage() );
      ccFilter->AddSeed( PRESS_SEED );
      ccFilter->SetLower( threshMin );
      ccFilter->SetUpper( threshMax );
      ccFilter->SetReplaceValue(1);
      ccFilter->Update();

      OverlayType::Pointer overlay = ccFilter->GetOutput();

      ImageType::RegionType croppedRegion = img->GetLargestPossibleRegion();
      ImageType::IndexType croppedIndex = croppedRegion.GetIndex();
      ImageType::SizeType croppedSize = croppedRegion.GetSize();
      double cropMaxSize = 0;
      for( unsigned int d=0; d<3; ++d )
        {
        double tf = seed[d] - PRESS_SEED[d];
        cropMaxSize += tf * tf;
        }
      int morphRadius = cropMaxSize / 5;
      if( morphRadius > 8 )
        {
        morphRadius = 8;
        }
      cropMaxSize = std::sqrt(cropMaxSize) * 6;
      for( unsigned int d=0; d<3; ++d )
        {
        int sizeD = (int)( cropMaxSize / img->GetSpacing()[d] + 0.5 );
        if( sizeD < 1 )
          {
          sizeD = 1;
          }
        int indxD = PRESS_SEED[d] - (sizeD/2);
        if( indxD < croppedIndex[d] )
          {
          sizeD -= (croppedIndex[d] - indxD);
          indxD = croppedIndex[d];
          }
        if( indxD + sizeD > croppedIndex[d] + croppedSize[d] )
          {
          sizeD = ( croppedIndex[d] + croppedSize[d] ) - indxD;
          }
        croppedSize[d] = sizeD;
        croppedIndex[d] = indxD;
        }
      croppedRegion.SetIndex( croppedIndex );
      croppedRegion.SetSize( croppedSize );

      if( morphRadius > 2 )
        {
        StructuringElementType dilateKernel;
        dilateKernel.SetRadius(morphRadius/2);
        dilateKernel.CreateStructuringElement();
        DilateFilterType::Pointer dilateFilter = DilateFilterType::New();
        dilateFilter->SetInput( overlay );
        dilateFilter->SetKernel( dilateKernel );
        dilateFilter->SetForegroundValue( 1 );
        dilateFilter->Update();

        StructuringElementType erodeKernel;
        erodeKernel.SetRadius(morphRadius);
        erodeKernel.CreateStructuringElement();
        ErodeFilterType::Pointer erodeFilter = ErodeFilterType::New();
        erodeFilter->SetInput( dilateFilter->GetOutput() );
        erodeFilter->SetKernel( erodeKernel );
        erodeFilter->SetForegroundValue( 1 );
        erodeFilter->Update();

        dilateFilter->SetInput( erodeFilter->GetOutput() );
        dilateFilter->SetKernel( dilateKernel );
        dilateFilter->SetForegroundValue( 1 );
        dilateFilter->Update();

        overlay = dilateFilter->GetOutput();
        }

      ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
      extractFilter->SetInput( overlay );
      extractFilter->SetExtractionRegion( croppedRegion );
      extractFilter->Update();
      OverlayType::Pointer croppedImg = extractFilter->GetOutput();

      InvertFilterType::Pointer invertFilter = InvertFilterType::New();
      invertFilter->SetInput( croppedImg );
      invertFilter->SetMaximum( 1 );
      invertFilter->Update();
      croppedImg = invertFilter->GetOutput();

      DistanceFilterType::Pointer distanceFilter =
        DistanceFilterType::New();
      distanceFilter->SetInput( croppedImg );
      distanceFilter->SetUseImageSpacing( true );
      distanceFilter->SetSquaredDistance( false );
      distanceFilter->Update();
      img = distanceFilter->GetOutput();

      double dMax = img->GetPixel(indx);
      ImageType::IndexType dMaxIndx = PRESS_SEED;
      itk::NeighborhoodIterator<ImageType> itD( seedRadius, img,
        img->GetLargestPossibleRegion() );
      itD.SetLocation( dMaxIndx );
      for( unsigned int i=0; i<itD.Size(); ++i )
        {
        double tf = itD.GetPixel(i);
        if( tf > dMax )
          {
          dMax = tf;
          dMaxIndx = itD.GetIndex(i);
          }
        }

      double zri = dMax / img->GetSpacing()[2];
      int zMin = dMaxIndx[2] - zri;
      if( zMin < croppedIndex[2] )
        {
        zMin = croppedIndex[2];
        }
      int zMax = dMaxIndx[2] + zri;
      if( zMax >= croppedIndex[2] + croppedSize[2] )
        {
        zMax = croppedIndex[2] + croppedSize[2] - 1;
        }
      double yri = dMax / img->GetSpacing()[1];
      int yMin = dMaxIndx[1] - yri;
      if( yMin < croppedIndex[1] )
        {
        yMin = croppedIndex[1];
        }
      int yMax = dMaxIndx[1] + yri;
      if( yMax >= croppedIndex[1] + croppedSize[1] )
        {
        yMax = croppedIndex[1] + croppedSize[1] - 1;
        }
      double xri = dMax / img->GetSpacing()[0];
      int xMin = dMaxIndx[0] - xri;
      if( xMin < croppedIndex[0] )
        {
        xMin = croppedIndex[0];
        }
      int xMax = dMaxIndx[0] + xri;
      if( xMax >= croppedIndex[0] + croppedSize[0] )
        {
        xMax = croppedIndex[0] + croppedSize[0] - 1;
        }
      for( indx[2]=zMin; indx[2]<=zMax; ++indx[2] )
        {
        double tf = (indx[2]-dMaxIndx[2])*img->GetSpacing()[2];
        double dz = (tf * tf);
        for( indx[1]=yMin; indx[1]<=yMax; ++indx[1] )
          {
          tf = (indx[1]-dMaxIndx[1])*img->GetSpacing()[1];
          double dy = (tf * tf);
          for( indx[0]=xMin; indx[0]<=xMax; ++indx[0] )
            {
            tf = (indx[0]-dMaxIndx[0])*img->GetSpacing()[0];
            double dx = (tf * tf);
            if( dz + dy + dx <= dMax * dMax )
              {
              overlay->SetPixel( indx, 3 );
              }
            }
          }
        }

      std::ostringstream sstr;
      sstr << "R = " << dMax;
      sv->setMessage( sstr.str() );

      sv->setInputOverlay( overlay );
      sv->update();
      }
    else if( sv->selectMovement() == SM_PRESS )
      {
      PRESS_SEED[0] = int( x );
      PRESS_SEED[1] = int( y );
      PRESS_SEED[2] = int( z );

      ImageType::Pointer img = sv->inputImage();

      double v = img->GetPixel( PRESS_SEED );

      PRESS_SEED_V = v;
      }
    }
}

int parseAndExecImageViewer(int argc, char* argv[])
{
  typedef itk::Image< double, 3 >        ImageType;
  typedef itk::Image< unsigned char, 3 > OverlayType;

  PARSE_ARGS;

  QApplication myApp(argc, argv);

  QtImageViewer viewer( 0 );
  viewer.setWindowTitle("ImageViewer");

  QString filePathToLoad;
  filePathToLoad = QString::fromStdString(inputImage);

  viewer.loadInputImage(filePathToLoad);

  if(!overlayImage.empty())
    {
    viewer.loadOverlayImage(QString::fromStdString(overlayImage));
    }
  viewer.sliceView()->setOrientation(orientation);
  if(sliceOffset != -1)
    {
    viewer.sliceView()->setSliceNum(sliceOffset);
    }
  if(minIntensityArg.isSet())
    {
    viewer.sliceView()->setIWMin(minIntensity);
    }
  if(maxIntensityArg.isSet())
    {
    viewer.sliceView()->setIWMax(maxIntensity);
    }

  viewer.sliceView()->setZoom(zoom);
  viewer.sliceView()->transpose(transpose);
  viewer.sliceView()->flipZ(zFlipped);
  viewer.sliceView()->flipY(!yFlipped);
  viewer.sliceView()->flipX(xFlipped);
  viewer.sliceView()->setOverlayOpacity(overlayOpacity);
  viewer.sliceView()->setViewCrosshairs(!crosshairs);
  viewer.sliceView()->setDisplayState(details);
  viewer.sliceView()->setViewValuePhysicalUnits(physicalUnits);
  viewer.sliceView()->setViewValue(!overlayValue);
  viewer.sliceView()->setViewAxisLabel(axisLabel);
  viewer.sliceView()->setViewClickedPoints(clickedPoints);
  viewer.sliceView()->setImageMode(imageMode.c_str());
  viewer.sliceView()->setIWModeMax(iwModeMax.c_str());
  viewer.sliceView()->setIWModeMin(iwModeMin.c_str());
  viewer.sliceView()->setClickSelectArgCallBack( myMouseCallback );
  viewer.sliceView()->setClickSelectArg( (void*)(viewer.sliceView()) );
  viewer.sliceView()->setKeyEventArgCallBack( myKeyCallback );
  viewer.sliceView()->setKeyEventArg( (void*)(viewer.sliceView()) );

  viewer.sliceView()->setPaintRadius( 10 );
  //viewer.sliceView()->setPaintColor( 0 );
  //viewer.sliceView()->setClickMode( CM_CUSTOM );

  ImageType::Pointer img = viewer.sliceView()->inputImage();

  typedef itk::StatisticsImageFilter<ImageType>  StatisticsFilterType;
  StatisticsFilterType::Pointer statsFilter = StatisticsFilterType::New();
  statsFilter->SetInput( img );
  statsFilter->Update();
  IMAGE_MIN = statsFilter->GetMinimum();
  IMAGE_MAX = statsFilter->GetMaximum();

  viewer.sliceView()->update();

  viewer.show();
  int execReturn;
  try
    {
    execReturn = myApp.exec();
    }
  catch (itk::ExceptionObject & e)
    {
    std::cerr << "Exception during GUI execution" << std::endl;
    std::cerr << e << std::endl;
    return EXIT_FAILURE;
    }
  return execReturn;
}

int main( int argc, char* argv[] )
{
  int res = EXIT_FAILURE;
  if(argc == 1)
    {
    res = execImageViewer(argc, argv);
    }
  else
    {
    res = parseAndExecImageViewer(argc, argv);
    }
  return res;
}
<commit_msg>ENH: Fix morphology radius to 2 voxels<commit_after>/*=========================================================================

Library:   TubeTK

Copyright 2010 Kitware Inc. 28 Corporate Drive,
Clifton Park, NY, 12065, USA.

All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=========================================================================*/
#include "ImageViewerConfigure.h"
#include "ImageViewerCLP.h"
//Qt includes
#include <QApplication>
#include <QDebug>
#include <QFileInfo>
#include <QKeyEvent>

#include <itkMedianImageFilter.h>
#include <itkConnectedThresholdImageFilter.h>
#include <itkExtractImageFilter.h>
#include <itkInvertIntensityImageFilter.h>
#include <itkSignedMaurerDistanceMapImageFilter.h>
#include <itkBinaryErodeImageFilter.h>
#include <itkBinaryDilateImageFilter.h>
#include <itkBinaryBallStructuringElement.h>
#include <itkMinimumMaximumImageCalculator.h>
#include <itkNeighborhoodIterator.h>
#include <itkStatisticsImageFilter.h>

//QtImageViewer includes
#include "QtGlSliceView.h"
#include "QtImageViewer.h"

int execImageViewer(int argc, char* argv[])
{
  QApplication myApp( argc, argv );

  QtImageViewer viewer( 0 );
  viewer.setWindowTitle("ImageViewer");
  viewer.loadInputImage();
  viewer.show();

  int execReturn;
  try
    {
    execReturn = myApp.exec();
    }
  catch (itk::ExceptionObject & e)
    {
    std::cerr << "Exception during GUI execution" << std::endl;
    std::cerr << e << std::endl;
    return EXIT_FAILURE;
    }
  return execReturn;
}

double IMAGE_MIN = 0;
double IMAGE_MAX = 1;

void myKeyCallback(QKeyEvent *event, void *d)
{
  if( event->key() == Qt::Key_Apostrophe )
    {
    QtGlSliceView * sv = (QtGlSliceView *)(d);

    typedef itk::Image< double, 3 >        ImageType;
    ImageType::Pointer img = sv->inputImage();

    typedef itk::MedianImageFilter< ImageType, ImageType >
                                           FilterType;
    FilterType::Pointer filter = FilterType::New();
    filter->SetInput( img );
    filter->SetRadius( 1 );
    filter->Update();
    sv->setInputImage( filter->GetOutput() );
    sv->update();
    }
}

void myMouseCallback(double x, double y, double z, double v, void *d)
{
  static itk::Index<3> PRESS_SEED;
  static double PRESS_SEED_V = 0;

  typedef itk::Image< double, 3 >        ImageType;
  typedef itk::Image< unsigned char, 3 > OverlayType;

  typedef itk::ConnectedThresholdImageFilter< ImageType, OverlayType >
                                         ConnCompFilterType;
  typedef itk::BinaryBallStructuringElement< unsigned char, 3 >
                                         StructuringElementType;
  typedef itk::BinaryErodeImageFilter< OverlayType, OverlayType,
                                         StructuringElementType >
                                         ErodeFilterType;
  typedef itk::BinaryDilateImageFilter< OverlayType, OverlayType,
                                         StructuringElementType >
                                         DilateFilterType;
  typedef itk::ExtractImageFilter< OverlayType, OverlayType >
                                         ExtractFilterType;
  typedef itk::InvertIntensityImageFilter< OverlayType, OverlayType >
                                         InvertFilterType;
  typedef itk::SignedMaurerDistanceMapImageFilter< OverlayType, ImageType >
                                         DistanceFilterType;
  typedef itk::MinimumMaximumImageCalculator< ImageType >
                                         MinMaxFilterType;

  QtGlSliceView * sv = (QtGlSliceView *)(d);

  if( sv->clickMode() == CM_CUSTOM )
    {
    if( sv->selectMovement() == SM_MOVE || sv->selectMovement() == SM_RELEASE )
      {
      itk::Index<3> seed;
      seed[0] = int( x );
      seed[1] = int( y );
      seed[2] = int( z );

      ImageType::Pointer img = sv->inputImage();

      double seedDistance = 0;
      for(unsigned int d=0; d<3; ++d)
        {
        double tf = seed[d] - PRESS_SEED[d];
        seedDistance += tf * tf;
        }
      seedDistance = std::sqrt(seedDistance);

      itk::NeighborhoodIterator<ImageType>::RadiusType seedRadius;
      for(unsigned int d=0; d<3; ++d)
        {
        seedRadius[d] = seedDistance/2;
        int limit = img->GetLargestPossibleRegion().GetSize()[d]/2-1;
        if( limit < 0 )
          {
          limit = 0;
          }
        if( seedRadius[d] > limit )
          {
          seedRadius[d] = limit;
          }
        }
      ImageType::IndexType indx = PRESS_SEED;

      double threshMin = PRESS_SEED_V;
      double threshMax = PRESS_SEED_V;
      itk::NeighborhoodIterator<ImageType> it( seedRadius, img,
        img->GetLargestPossibleRegion() );
      it.SetLocation( indx );
      for( unsigned int i=0; i<it.Size(); ++i )
        {
        double tf = it.GetPixel(i);
        if( tf < threshMin )
          {
          threshMin = tf;
          }
        else if( tf > threshMax )
          {
          threshMax = tf;
          }
        }

      double v = img->GetPixel(seed);
      if( v < threshMin )
        {
        threshMin = v;
        }
      else if( v > threshMax )
        {
        threshMax = v;
        }

      ConnCompFilterType::Pointer ccFilter = ConnCompFilterType::New();
      ccFilter->SetInput( sv->inputImage() );
      ccFilter->AddSeed( PRESS_SEED );
      ccFilter->SetLower( threshMin );
      ccFilter->SetUpper( threshMax );
      ccFilter->SetReplaceValue(1);
      ccFilter->Update();

      OverlayType::Pointer overlay = ccFilter->GetOutput();

      ImageType::RegionType croppedRegion = img->GetLargestPossibleRegion();
      ImageType::IndexType croppedIndex = croppedRegion.GetIndex();
      ImageType::SizeType croppedSize = croppedRegion.GetSize();
      double cropMaxSize = 0;
      for( unsigned int d=0; d<3; ++d )
        {
        double tf = seed[d] - PRESS_SEED[d];
        cropMaxSize += tf * tf;
        }
      int morphRadius = 2; //cropMaxSize / 5;
      if( morphRadius > 8 )
        {
        morphRadius = 8;
        }
      cropMaxSize = std::sqrt(cropMaxSize) * 6;
      for( unsigned int d=0; d<3; ++d )
        {
        int sizeD = (int)( cropMaxSize / img->GetSpacing()[d] + 0.5 );
        if( sizeD < 1 )
          {
          sizeD = 1;
          }
        int indxD = PRESS_SEED[d] - (sizeD/2);
        if( indxD < croppedIndex[d] )
          {
          sizeD -= (croppedIndex[d] - indxD);
          indxD = croppedIndex[d];
          }
        if( indxD + sizeD > croppedIndex[d] + croppedSize[d] )
          {
          sizeD = ( croppedIndex[d] + croppedSize[d] ) - indxD;
          }
        croppedSize[d] = sizeD;
        croppedIndex[d] = indxD;
        }
      croppedRegion.SetIndex( croppedIndex );
      croppedRegion.SetSize( croppedSize );

      if( morphRadius > 2 )
        {
        StructuringElementType dilateKernel;
        dilateKernel.SetRadius(morphRadius/2);
        dilateKernel.CreateStructuringElement();
        DilateFilterType::Pointer dilateFilter = DilateFilterType::New();
        dilateFilter->SetInput( overlay );
        dilateFilter->SetKernel( dilateKernel );
        dilateFilter->SetForegroundValue( 1 );
        dilateFilter->Update();

        StructuringElementType erodeKernel;
        erodeKernel.SetRadius(morphRadius);
        erodeKernel.CreateStructuringElement();
        ErodeFilterType::Pointer erodeFilter = ErodeFilterType::New();
        erodeFilter->SetInput( dilateFilter->GetOutput() );
        erodeFilter->SetKernel( erodeKernel );
        erodeFilter->SetForegroundValue( 1 );
        erodeFilter->Update();

        dilateFilter->SetInput( erodeFilter->GetOutput() );
        dilateFilter->SetKernel( dilateKernel );
        dilateFilter->SetForegroundValue( 1 );
        dilateFilter->Update();

        overlay = dilateFilter->GetOutput();
        }

      ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
      extractFilter->SetInput( overlay );
      extractFilter->SetExtractionRegion( croppedRegion );
      extractFilter->Update();
      OverlayType::Pointer croppedImg = extractFilter->GetOutput();

      InvertFilterType::Pointer invertFilter = InvertFilterType::New();
      invertFilter->SetInput( croppedImg );
      invertFilter->SetMaximum( 1 );
      invertFilter->Update();
      croppedImg = invertFilter->GetOutput();

      DistanceFilterType::Pointer distanceFilter =
        DistanceFilterType::New();
      distanceFilter->SetInput( croppedImg );
      distanceFilter->SetUseImageSpacing( true );
      distanceFilter->SetSquaredDistance( false );
      distanceFilter->Update();
      img = distanceFilter->GetOutput();

      double dMax = img->GetPixel(indx);
      ImageType::IndexType dMaxIndx = PRESS_SEED;
      itk::NeighborhoodIterator<ImageType> itD( seedRadius, img,
        img->GetLargestPossibleRegion() );
      itD.SetLocation( dMaxIndx );
      for( unsigned int i=0; i<itD.Size(); ++i )
        {
        double tf = itD.GetPixel(i);
        if( tf > dMax )
          {
          dMax = tf;
          dMaxIndx = itD.GetIndex(i);
          }
        }

      double zri = dMax / img->GetSpacing()[2];
      int zMin = dMaxIndx[2] - zri;
      if( zMin < croppedIndex[2] )
        {
        zMin = croppedIndex[2];
        }
      int zMax = dMaxIndx[2] + zri;
      if( zMax >= croppedIndex[2] + croppedSize[2] )
        {
        zMax = croppedIndex[2] + croppedSize[2] - 1;
        }
      double yri = dMax / img->GetSpacing()[1];
      int yMin = dMaxIndx[1] - yri;
      if( yMin < croppedIndex[1] )
        {
        yMin = croppedIndex[1];
        }
      int yMax = dMaxIndx[1] + yri;
      if( yMax >= croppedIndex[1] + croppedSize[1] )
        {
        yMax = croppedIndex[1] + croppedSize[1] - 1;
        }
      double xri = dMax / img->GetSpacing()[0];
      int xMin = dMaxIndx[0] - xri;
      if( xMin < croppedIndex[0] )
        {
        xMin = croppedIndex[0];
        }
      int xMax = dMaxIndx[0] + xri;
      if( xMax >= croppedIndex[0] + croppedSize[0] )
        {
        xMax = croppedIndex[0] + croppedSize[0] - 1;
        }
      for( indx[2]=zMin; indx[2]<=zMax; ++indx[2] )
        {
        double tf = (indx[2]-dMaxIndx[2])*img->GetSpacing()[2];
        double dz = (tf * tf);
        for( indx[1]=yMin; indx[1]<=yMax; ++indx[1] )
          {
          tf = (indx[1]-dMaxIndx[1])*img->GetSpacing()[1];
          double dy = (tf * tf);
          for( indx[0]=xMin; indx[0]<=xMax; ++indx[0] )
            {
            tf = (indx[0]-dMaxIndx[0])*img->GetSpacing()[0];
            double dx = (tf * tf);
            if( dz + dy + dx <= dMax * dMax )
              {
              overlay->SetPixel( indx, 3 );
              }
            }
          }
        }

      std::ostringstream sstr;
      sstr << "R = " << dMax;
      sv->setMessage( sstr.str() );

      sv->setInputOverlay( overlay );
      sv->update();
      }
    else if( sv->selectMovement() == SM_PRESS )
      {
      PRESS_SEED[0] = int( x );
      PRESS_SEED[1] = int( y );
      PRESS_SEED[2] = int( z );

      ImageType::Pointer img = sv->inputImage();

      double v = img->GetPixel( PRESS_SEED );

      PRESS_SEED_V = v;
      }
    }
}

int parseAndExecImageViewer(int argc, char* argv[])
{
  typedef itk::Image< double, 3 >        ImageType;
  typedef itk::Image< unsigned char, 3 > OverlayType;

  PARSE_ARGS;

  QApplication myApp(argc, argv);

  QtImageViewer viewer( 0 );
  viewer.setWindowTitle("ImageViewer");

  QString filePathToLoad;
  filePathToLoad = QString::fromStdString(inputImage);

  viewer.loadInputImage(filePathToLoad);

  if(!overlayImage.empty())
    {
    viewer.loadOverlayImage(QString::fromStdString(overlayImage));
    }
  viewer.sliceView()->setOrientation(orientation);
  if(sliceOffset != -1)
    {
    viewer.sliceView()->setSliceNum(sliceOffset);
    }
  if(minIntensityArg.isSet())
    {
    viewer.sliceView()->setIWMin(minIntensity);
    }
  if(maxIntensityArg.isSet())
    {
    viewer.sliceView()->setIWMax(maxIntensity);
    }

  viewer.sliceView()->setZoom(zoom);
  viewer.sliceView()->transpose(transpose);
  viewer.sliceView()->flipZ(zFlipped);
  viewer.sliceView()->flipY(!yFlipped);
  viewer.sliceView()->flipX(xFlipped);
  viewer.sliceView()->setOverlayOpacity(overlayOpacity);
  viewer.sliceView()->setViewCrosshairs(!crosshairs);
  viewer.sliceView()->setDisplayState(details);
  viewer.sliceView()->setViewValuePhysicalUnits(physicalUnits);
  viewer.sliceView()->setViewValue(!overlayValue);
  viewer.sliceView()->setViewAxisLabel(axisLabel);
  viewer.sliceView()->setViewClickedPoints(clickedPoints);
  viewer.sliceView()->setImageMode(imageMode.c_str());
  viewer.sliceView()->setIWModeMax(iwModeMax.c_str());
  viewer.sliceView()->setIWModeMin(iwModeMin.c_str());
  viewer.sliceView()->setClickSelectArgCallBack( myMouseCallback );
  viewer.sliceView()->setClickSelectArg( (void*)(viewer.sliceView()) );
  viewer.sliceView()->setKeyEventArgCallBack( myKeyCallback );
  viewer.sliceView()->setKeyEventArg( (void*)(viewer.sliceView()) );

  viewer.sliceView()->setPaintRadius( 10 );
  //viewer.sliceView()->setPaintColor( 0 );
  //viewer.sliceView()->setClickMode( CM_CUSTOM );

  ImageType::Pointer img = viewer.sliceView()->inputImage();

  typedef itk::StatisticsImageFilter<ImageType>  StatisticsFilterType;
  StatisticsFilterType::Pointer statsFilter = StatisticsFilterType::New();
  statsFilter->SetInput( img );
  statsFilter->Update();
  IMAGE_MIN = statsFilter->GetMinimum();
  IMAGE_MAX = statsFilter->GetMaximum();

  viewer.sliceView()->update();

  viewer.show();
  int execReturn;
  try
    {
    execReturn = myApp.exec();
    }
  catch (itk::ExceptionObject & e)
    {
    std::cerr << "Exception during GUI execution" << std::endl;
    std::cerr << e << std::endl;
    return EXIT_FAILURE;
    }
  return execReturn;
}

int main( int argc, char* argv[] )
{
  int res = EXIT_FAILURE;
  if(argc == 1)
    {
    res = execImageViewer(argc, argv);
    }
  else
    {
    res = parseAndExecImageViewer(argc, argv);
    }
  return res;
}
<|endoftext|>


<commit_before>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/fastos/fastos.h>
#include <vespa/log/log.h>
LOG_SETUP("attribute_reprocessing_initializer_test");

#include <vespa/searchcore/proton/attribute/attribute_populator.h>
#include <vespa/searchcore/proton/attribute/attributemanager.h>
#include <vespa/searchcore/proton/attribute/document_field_populator.h>
#include <vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.h>
#include <vespa/searchcore/proton/reprocessing/i_reprocessing_handler.h>
#include <vespa/searchcore/proton/test/attribute_utils.h>
#include <vespa/searchcore/proton/test/directory_handler.h>
#include <vespa/searchlib/index/dummyfileheadercontext.h>
#include <vespa/vespalib/test/insertion_operators.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchlib/common/foregroundtaskexecutor.h>

using namespace proton;
using namespace search;
using namespace search::index;

const vespalib::string TEST_DIR = "test_output";
const SerialNum INIT_SERIAL_NUM = 10;
typedef std::vector<vespalib::string> StringVector;
typedef std::set<vespalib::string> StringSet;
typedef AttributeReprocessingInitializer::Config ARIConfig;

struct MyReprocessingHandler : public IReprocessingHandler
{
    IReprocessingReader::SP _reader;
    std::vector<IReprocessingRewriter::SP> _rewriters;
    MyReprocessingHandler() : _reader(), _rewriters() {}
    virtual void addReader(const IReprocessingReader::SP &reader) {
        _reader = reader;
    }
    virtual void addRewriter(const IReprocessingRewriter::SP &rewriter) {
        _rewriters.push_back(rewriter);
    }
};

struct MyDocTypeInspector : public IDocumentTypeInspector
{
    typedef std::shared_ptr<MyDocTypeInspector> SP;
    std::set<vespalib::string> _fields;
    MyDocTypeInspector() : _fields() {}
    virtual bool hasField(const vespalib::string &name) const {
        return _fields.count(name) > 0;
    }
};

struct MyConfig
{
    DummyFileHeaderContext _fileHeaderContext;
    ForegroundTaskExecutor _attributeFieldWriter;
    HwInfo _hwInfo;
    AttributeManager::SP _mgr;
    search::index::Schema _schema;
    MyDocTypeInspector::SP _inspector;
    MyConfig();
    ~MyConfig();
    void addFields(const StringVector &fields) {
        for (auto field : fields) {
            _inspector->_fields.insert(field);
        }
    }
    void addAttrs(const StringVector &attrs) {
        for (auto attr : attrs) {
            if (attr == "tensor") {
                _mgr->addAttribute(attr, test::AttributeUtils::getTensorConfig(), 1);
                _schema.addAttributeField(Schema::AttributeField(attr, schema::TENSOR));
            } else if (attr == "predicate") {
                _mgr->addAttribute(attr, test::AttributeUtils::getPredicateConfig(), 1);
                _schema.addAttributeField(Schema::AttributeField(attr, schema::BOOLEANTREE));
            } else {
                _mgr->addAttribute(attr, test::AttributeUtils::getStringConfig(), 1);
                _schema.addAttributeField(Schema::AttributeField(attr, schema::STRING));
            }
        }
    }
    void addIndexField(const vespalib::string &name) {
        _schema.addIndexField(Schema::IndexField(name, schema::STRING));
    }
};

MyConfig::MyConfig()
    : _fileHeaderContext(),
      _attributeFieldWriter(),
      _hwInfo(),
      _mgr(new AttributeManager(TEST_DIR, "test.subdb", TuneFileAttributes(),
                                _fileHeaderContext,
                                _attributeFieldWriter, _hwInfo)),
      _schema(),
      _inspector(new MyDocTypeInspector())
{}
MyConfig::~MyConfig() {}

struct Fixture
{
    test::DirectoryHandler _dirHandler;
    DummyFileHeaderContext _fileHeaderContext;
    ForegroundTaskExecutor _attributeFieldWriter;
    HwInfo _hwInfo;
    AttributeManager::SP _mgr;
    MyConfig _oldCfg;
    MyConfig _newCfg;
    AttributeReprocessingInitializer::UP _initializer;
    MyReprocessingHandler _handler;
    Fixture()
        : _dirHandler(TEST_DIR),
          _fileHeaderContext(),
          _attributeFieldWriter(),
          _hwInfo(),
          _mgr(new AttributeManager(TEST_DIR, "test.subdb", TuneFileAttributes(),
                                    _fileHeaderContext,
                                    _attributeFieldWriter, _hwInfo)),
          _initializer(),
          _handler()
    {
    }
    void init() {
        _initializer.reset(new AttributeReprocessingInitializer
                (ARIConfig(_newCfg._mgr, _newCfg._schema, _newCfg._inspector),
                 ARIConfig(_oldCfg._mgr, _oldCfg._schema, _oldCfg._inspector), "test", INIT_SERIAL_NUM));
        _initializer->initialize(_handler);
    }
    Fixture &addOldConfig(const StringVector &fields,
                          const StringVector &attrs) {
        return addConfig(fields, attrs, _oldCfg);
    }
    Fixture &addNewConfig(const StringVector &fields,
                          const StringVector &attrs) {
        return addConfig(fields, attrs, _newCfg);
    }
    Fixture &addConfig(const StringVector &fields,
                       const StringVector &attrs,
                       MyConfig &cfg) {
        cfg.addFields(fields);
        cfg.addAttrs(attrs);
        return *this;
    }
    bool assertAttributes(const StringSet &expAttrs) {
        if (expAttrs.empty()) {
            if (!EXPECT_TRUE(_handler._reader.get() == nullptr)) return false;
        } else {
            const AttributePopulator &populator =
                    dynamic_cast<const AttributePopulator &>(*_handler._reader);
            std::vector<search::AttributeVector *> attrList =
                populator.getWriter().getWritableAttributes();
            std::set<vespalib::string> actAttrs;
            for (const auto attr : attrList) {
                actAttrs.insert(attr->getName());
            }
            if (!EXPECT_EQUAL(expAttrs, actAttrs)) return false;
        }
        return true;
    }
    bool assertFields(const StringSet &expFields) {
        if (expFields.empty()) {
            if (!EXPECT_EQUAL(0u, _handler._rewriters.size())) return false;
        } else {
            StringSet actFields;
            for (auto rewriter : _handler._rewriters) {
                const DocumentFieldPopulator &populator =
                    dynamic_cast<const DocumentFieldPopulator &>(*rewriter);
                actFields.insert(populator.getAttribute().getName());
            }
            if (!EXPECT_EQUAL(expFields, actFields)) return false;
        }
        return true;
    }
};

TEST_F("require that new field does NOT require attribute populate", Fixture)
{
    f.addOldConfig({}, {}).addNewConfig({"a"}, {"a"}).init();
    EXPECT_TRUE(f.assertAttributes({}));
}

TEST_F("require that added attribute aspect does require attribute populate", Fixture)
{
    f.addOldConfig({"a"}, {}).addNewConfig({"a"}, {"a"}).init();
    EXPECT_TRUE(f.assertAttributes({"a"}));
}

TEST_F("require that initializer can setup populate of several attributes", Fixture)
{
    f.addOldConfig({"a", "b", "c", "d"}, {"a", "b"}).
            addNewConfig({"a", "b", "c", "d"}, {"a", "b", "c", "d"}).init();
    EXPECT_TRUE(f.assertAttributes({"c", "d"}));
}

TEST_F("require that new field does NOT require document field populate", Fixture)
{
    f.addOldConfig({}, {}).addNewConfig({"a"}, {"a"}).init();
    EXPECT_TRUE(f.assertFields({}));
}

TEST_F("require that removed field does NOT require document field populate", Fixture)
{
    f.addOldConfig({"a"}, {"a"}).addNewConfig({}, {}).init();
    EXPECT_TRUE(f.assertFields({}));
}

TEST_F("require that removed attribute aspect does require document field populate", Fixture)
{
    f.addOldConfig({"a"}, {"a"}).addNewConfig({"a"}, {}).init();
    EXPECT_TRUE(f.assertFields({"a"}));
}

TEST_F("require that removed attribute aspect (when also index field) does NOT require document field populate",
        Fixture)
{
    f.addOldConfig({"a"}, {"a"}).addNewConfig({"a"}, {});
    f._oldCfg.addIndexField("a");
    f._newCfg.addIndexField("a");
    f.init();
    EXPECT_TRUE(f.assertFields({}));
}

TEST_F("require that initializer can setup populate of several document fields", Fixture)
{
    f.addOldConfig({"a", "b", "c", "d"}, {"a", "b", "c", "d"}).
            addNewConfig({"a", "b", "c", "d"}, {"a", "b"}).init();
    EXPECT_TRUE(f.assertFields({"c", "d"}));
}

TEST_F("require that initializer can setup both attribute and document field populate", Fixture)
{
    f.addOldConfig({"a", "b"}, {"a"}).
            addNewConfig({"a", "b"}, {"b"}).init();
    EXPECT_TRUE(f.assertAttributes({"b"}));
    EXPECT_TRUE(f.assertFields({"a"}));
}

TEST_F("require that tensor fields are not populated from attribute", Fixture)
{
    f.addOldConfig({"a", "b", "c", "d", "tensor"},
                   {"a", "b", "c", "d", "tensor"}).
        addNewConfig({"a", "b", "c", "d", "tensor"}, {"a", "b"}).init();
    EXPECT_TRUE(f.assertFields({"c", "d"}));
}

TEST_F("require that predicate fields are not populated from attribute", Fixture)
{
    f.addOldConfig({"a", "b", "c", "d", "predicate"},
                   {"a", "b", "c", "d", "predicate"}).
        addNewConfig({"a", "b", "c", "d", "predicate"}, {"a", "b"}).init();
    EXPECT_TRUE(f.assertFields({"c", "d"}));
}

TEST_MAIN()
{
    TEST_RUN_ALL();
}
<commit_msg>Test that reprocessing is skipped for attributes that were flushed after earlier reprocessing.<commit_after>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/fastos/fastos.h>
#include <vespa/log/log.h>
LOG_SETUP("attribute_reprocessing_initializer_test");

#include <vespa/searchcore/proton/attribute/attribute_populator.h>
#include <vespa/searchcore/proton/attribute/attributemanager.h>
#include <vespa/searchcore/proton/attribute/attributedisklayout.h>
#include <vespa/searchcore/proton/attribute/attribute_directory.h>
#include <vespa/searchcore/proton/attribute/document_field_populator.h>
#include <vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.h>
#include <vespa/searchcore/proton/reprocessing/i_reprocessing_handler.h>
#include <vespa/searchcore/proton/test/attribute_utils.h>
#include <vespa/searchcore/proton/test/directory_handler.h>
#include <vespa/searchlib/index/dummyfileheadercontext.h>
#include <vespa/searchlib/attribute/attributefactory.h>
#include <vespa/vespalib/test/insertion_operators.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchlib/common/foregroundtaskexecutor.h>

using namespace proton;
using namespace search;
using namespace search::index;
using search::attribute::Config;
using search::attribute::BasicType;

const vespalib::string TEST_DIR = "test_output";
const SerialNum INIT_SERIAL_NUM = 10;
typedef std::vector<vespalib::string> StringVector;
typedef std::set<vespalib::string> StringSet;
typedef AttributeReprocessingInitializer::Config ARIConfig;

struct MyReprocessingHandler : public IReprocessingHandler
{
    IReprocessingReader::SP _reader;
    std::vector<IReprocessingRewriter::SP> _rewriters;
    MyReprocessingHandler() : _reader(), _rewriters() {}
    virtual void addReader(const IReprocessingReader::SP &reader) {
        _reader = reader;
    }
    virtual void addRewriter(const IReprocessingRewriter::SP &rewriter) {
        _rewriters.push_back(rewriter);
    }
};

struct MyDocTypeInspector : public IDocumentTypeInspector
{
    typedef std::shared_ptr<MyDocTypeInspector> SP;
    std::set<vespalib::string> _fields;
    MyDocTypeInspector() : _fields() {}
    virtual bool hasField(const vespalib::string &name) const {
        return _fields.count(name) > 0;
    }
};

struct MyConfig
{
    DummyFileHeaderContext _fileHeaderContext;
    ForegroundTaskExecutor _attributeFieldWriter;
    HwInfo _hwInfo;
    AttributeManager::SP _mgr;
    search::index::Schema _schema;
    MyDocTypeInspector::SP _inspector;
    MyConfig();
    ~MyConfig();
    void addFields(const StringVector &fields) {
        for (auto field : fields) {
            _inspector->_fields.insert(field);
        }
    }
    void addAttrs(const StringVector &attrs) {
        for (auto attr : attrs) {
            if (attr == "tensor") {
                _mgr->addAttribute(attr, test::AttributeUtils::getTensorConfig(), 1);
                _schema.addAttributeField(Schema::AttributeField(attr, schema::TENSOR));
            } else if (attr == "predicate") {
                _mgr->addAttribute(attr, test::AttributeUtils::getPredicateConfig(), 1);
                _schema.addAttributeField(Schema::AttributeField(attr, schema::BOOLEANTREE));
            } else {
                _mgr->addAttribute(attr, test::AttributeUtils::getStringConfig(), 1);
                _schema.addAttributeField(Schema::AttributeField(attr, schema::STRING));
            }
        }
    }
    void addIndexField(const vespalib::string &name) {
        _schema.addIndexField(Schema::IndexField(name, schema::STRING));
    }
};

MyConfig::MyConfig()
    : _fileHeaderContext(),
      _attributeFieldWriter(),
      _hwInfo(),
      _mgr(new AttributeManager(TEST_DIR, "test.subdb", TuneFileAttributes(),
                                _fileHeaderContext,
                                _attributeFieldWriter, _hwInfo)),
      _schema(),
      _inspector(new MyDocTypeInspector())
{}
MyConfig::~MyConfig() {}

struct Fixture
{
    test::DirectoryHandler _dirHandler;
    DummyFileHeaderContext _fileHeaderContext;
    ForegroundTaskExecutor _attributeFieldWriter;
    HwInfo _hwInfo;
    AttributeManager::SP _mgr;
    MyConfig _oldCfg;
    MyConfig _newCfg;
    AttributeReprocessingInitializer::UP _initializer;
    MyReprocessingHandler _handler;
    Fixture()
        : _dirHandler(TEST_DIR),
          _fileHeaderContext(),
          _attributeFieldWriter(),
          _hwInfo(),
          _mgr(new AttributeManager(TEST_DIR, "test.subdb", TuneFileAttributes(),
                                    _fileHeaderContext,
                                    _attributeFieldWriter, _hwInfo)),
          _initializer(),
          _handler()
    {
    }
    ~Fixture() { }
    void init() {
        _initializer.reset(new AttributeReprocessingInitializer
                (ARIConfig(_newCfg._mgr, _newCfg._schema, _newCfg._inspector),
                 ARIConfig(_oldCfg._mgr, _oldCfg._schema, _oldCfg._inspector), "test", INIT_SERIAL_NUM));
        _initializer->initialize(_handler);
    }
    Fixture &addOldConfig(const StringVector &fields,
                          const StringVector &attrs) {
        return addConfig(fields, attrs, _oldCfg);
    }
    Fixture &addNewConfig(const StringVector &fields,
                          const StringVector &attrs) {
        return addConfig(fields, attrs, _newCfg);
    }
    Fixture &addConfig(const StringVector &fields,
                       const StringVector &attrs,
                       MyConfig &cfg) {
        cfg.addFields(fields);
        cfg.addAttrs(attrs);
        return *this;
    }
    bool assertAttributes(const StringSet &expAttrs) {
        if (expAttrs.empty()) {
            if (!EXPECT_TRUE(_handler._reader.get() == nullptr)) return false;
        } else {
            const AttributePopulator &populator =
                    dynamic_cast<const AttributePopulator &>(*_handler._reader);
            std::vector<search::AttributeVector *> attrList =
                populator.getWriter().getWritableAttributes();
            std::set<vespalib::string> actAttrs;
            for (const auto attr : attrList) {
                actAttrs.insert(attr->getName());
            }
            if (!EXPECT_EQUAL(expAttrs, actAttrs)) return false;
        }
        return true;
    }
    bool assertFields(const StringSet &expFields) {
        if (expFields.empty()) {
            if (!EXPECT_EQUAL(0u, _handler._rewriters.size())) return false;
        } else {
            StringSet actFields;
            for (auto rewriter : _handler._rewriters) {
                const DocumentFieldPopulator &populator =
                    dynamic_cast<const DocumentFieldPopulator &>(*rewriter);
                actFields.insert(populator.getAttribute().getName());
            }
            if (!EXPECT_EQUAL(expFields, actFields)) return false;
        }
        return true;
    }
};

TEST_F("require that new field does NOT require attribute populate", Fixture)
{
    f.addOldConfig({}, {}).addNewConfig({"a"}, {"a"}).init();
    EXPECT_TRUE(f.assertAttributes({}));
}

TEST_F("require that added attribute aspect does require attribute populate", Fixture)
{
    f.addOldConfig({"a"}, {}).addNewConfig({"a"}, {"a"}).init();
    EXPECT_TRUE(f.assertAttributes({"a"}));
}

TEST_F("require that initializer can setup populate of several attributes", Fixture)
{
    f.addOldConfig({"a", "b", "c", "d"}, {"a", "b"}).
            addNewConfig({"a", "b", "c", "d"}, {"a", "b", "c", "d"}).init();
    EXPECT_TRUE(f.assertAttributes({"c", "d"}));
}

TEST_F("require that new field does NOT require document field populate", Fixture)
{
    f.addOldConfig({}, {}).addNewConfig({"a"}, {"a"}).init();
    EXPECT_TRUE(f.assertFields({}));
}

TEST_F("require that removed field does NOT require document field populate", Fixture)
{
    f.addOldConfig({"a"}, {"a"}).addNewConfig({}, {}).init();
    EXPECT_TRUE(f.assertFields({}));
}

TEST_F("require that removed attribute aspect does require document field populate", Fixture)
{
    f.addOldConfig({"a"}, {"a"}).addNewConfig({"a"}, {}).init();
    EXPECT_TRUE(f.assertFields({"a"}));
}

TEST_F("require that removed attribute aspect (when also index field) does NOT require document field populate",
        Fixture)
{
    f.addOldConfig({"a"}, {"a"}).addNewConfig({"a"}, {});
    f._oldCfg.addIndexField("a");
    f._newCfg.addIndexField("a");
    f.init();
    EXPECT_TRUE(f.assertFields({}));
}

TEST_F("require that initializer can setup populate of several document fields", Fixture)
{
    f.addOldConfig({"a", "b", "c", "d"}, {"a", "b", "c", "d"}).
            addNewConfig({"a", "b", "c", "d"}, {"a", "b"}).init();
    EXPECT_TRUE(f.assertFields({"c", "d"}));
}

TEST_F("require that initializer can setup both attribute and document field populate", Fixture)
{
    f.addOldConfig({"a", "b"}, {"a"}).
            addNewConfig({"a", "b"}, {"b"}).init();
    EXPECT_TRUE(f.assertAttributes({"b"}));
    EXPECT_TRUE(f.assertFields({"a"}));
}

TEST_F("require that tensor fields are not populated from attribute", Fixture)
{
    f.addOldConfig({"a", "b", "c", "d", "tensor"},
                   {"a", "b", "c", "d", "tensor"}).
        addNewConfig({"a", "b", "c", "d", "tensor"}, {"a", "b"}).init();
    EXPECT_TRUE(f.assertFields({"c", "d"}));
}

TEST_F("require that predicate fields are not populated from attribute", Fixture)
{
    f.addOldConfig({"a", "b", "c", "d", "predicate"},
                   {"a", "b", "c", "d", "predicate"}).
        addNewConfig({"a", "b", "c", "d", "predicate"}, {"a", "b"}).init();
    EXPECT_TRUE(f.assertFields({"c", "d"}));
}

TEST("require that added attribute aspect with flushed attribute after interruptted reprocessing does not require attribute populate")
{
    {
        auto diskLayout = AttributeDiskLayout::create(TEST_DIR);
        auto dir = diskLayout->createAttributeDir("a");
        auto writer = dir->getWriter();
        writer->createInvalidSnapshot(INIT_SERIAL_NUM);
        writer->markValidSnapshot(INIT_SERIAL_NUM);
        auto snapshotdir = writer->getSnapshotDir(INIT_SERIAL_NUM);
        vespalib::mkdir(snapshotdir);
        auto av = AttributeFactory::createAttribute(snapshotdir + "/a",
                                                    Config(BasicType::STRING));
        av->save();
    }
    Fixture f;
    f.addOldConfig({"a"}, {}).addNewConfig({"a"}, {"a"}).init();
    EXPECT_TRUE(f.assertAttributes({}));
}

TEST_MAIN()
{
    TEST_RUN_ALL();
}
<|endoftext|>