<commit_before>/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/

//  Software Guide : BeginCommandLineArgs
//    INPUTS:  {BrainProtonDensitySlice.png}
//    OUTPUTS: {ResampleImageFilterOutput1.png}
//    ARGUMENTS:    0
//  Software Guide : EndCommandLineArgs
//  Software Guide : BeginCommandLineArgs
//    INPUTS:  {BrainProtonDensitySlice.png}
//    OUTPUTS: {ResampleImageFilterOutput2.png}
//    ARGUMENTS:    1
//  Software Guide : EndCommandLineArgs
//  Software Guide : BeginCommandLineArgs
//    INPUTS:  {BrainProtonDensitySlice.png}
//    OUTPUTS: {ResampleImageFilterOutput3.png}
//    ARGUMENTS:    2
//  Software Guide : EndCommandLineArgs
//  Software Guide : BeginCommandLineArgs
//    INPUTS:  {BrainProtonDensitySlice.png}
//    OUTPUTS: {ResampleImageFilterOutput4.png}
//    ARGUMENTS:    3
//  Software Guide : EndCommandLineArgs
//
//  Software Guide : BeginLatex
//
//  Resampling an image is a very important task in image analysis. It is
//  especially important in the frame of image registration. The
//  \doxygen{ResampleImageFilter} implements image resampling through the use
//  of \doxygen{Transform}s. The inputs expected by this filter are an image,
//  a transform and an interpolator. The space coordinates of the image are
//  mapped through the transform in order to generate a new image. The extent
//  and spacing of the resulting image are selected by the user. Resampling
//  is performed in space coordinates, not pixel/grid coordinates. It is
//  quite important to ensure that image spacing is properly set on the
//  images involved. The interpolator is required since the mapping from one
//  space to the other will often require evaluation of the intensity of the
//  image at non-grid positions.
//
//  \index{itk::ResampleImageFilter}
//
//  Software Guide : EndLatex

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"

//  Software Guide : BeginLatex
//
//  The header file corresponding to this filter should be included first.
//
//  \index{itk::ResampleImageFilter!header}
//
//  Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkResampleImageFilter.h"
// Software Guide : EndCodeSnippet

//  Software Guide : BeginLatex
//
//  The header files corresponding to the transform and interpolator must
//  also be included.
//
//  \index{itk::AffineTransform!header}
//  \index{itk::Nearest\-Neighbor\-Interpolate\-Image\-Function!header}
//
//  Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkAffineTransform.h"
#include "itkNearestNeighborInterpolateImageFunction.h"
// Software Guide : EndCodeSnippet

int
main(int argc, char * argv[])
{
  if (argc < 4)
  {
    std::cerr << "Usage: " << std::endl;
    std::cerr << argv[0] << "  inputImageFile  outputImageFile";
    std::cerr << "  [exampleAction={0,1,2,3}]" << std::endl;
    return EXIT_FAILURE;
  }

  int exampleAction = 0;

  if (argc >= 4)
  {
    exampleAction = std::stoi(argv[3]);
  }

  //  Software Guide : BeginLatex
  //
  //  The dimension and pixel types for input and output image must be
  //  defined and with them the image types can be instantiated.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  constexpr unsigned int Dimension = 2;
  using InputPixelType = unsigned char;
  using OutputPixelType = unsigned char;
  using InputImageType = itk::Image<InputPixelType, Dimension>;
  using OutputImageType = itk::Image<OutputPixelType, Dimension>;
  // Software Guide : EndCodeSnippet

  using ReaderType = itk::ImageFileReader<InputImageType>;
  using WriterType = itk::ImageFileWriter<OutputImageType>;

  ReaderType::Pointer reader = ReaderType::New();
  WriterType::Pointer writer = WriterType::New();

  reader->SetFileName(argv[1]);
  writer->SetFileName(argv[2]);

  //  Software Guide : BeginLatex
  //
  //  Using the image and transform types it is now possible to instantiate
  //  the filter type and create the filter object.
  //
  //  \index{itk::ResampleImageFilter!instantiation}
  //  \index{itk::ResampleImageFilter!New()}
  //  \index{itk::ResampleImageFilter!Pointer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  using FilterType =
    itk::ResampleImageFilter<InputImageType, OutputImageType>;
  FilterType::Pointer filter = FilterType::New();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The transform type is typically defined using the image dimension
  //  and the type used for representing space coordinates.
  //
  //  \index{itk::AffineTransform!instantiation}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  using TransformType = itk::AffineTransform<double, Dimension>;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  An instance of the transform object is instantiated and passed to the
  //  resample filter. By default, the parameters of the transform are set to
  //  represent the identity transform.
  //
  //  \index{itk::ResampleImageFilter!SetTransform()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  TransformType::Pointer transform = TransformType::New();
  filter->SetTransform(transform);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The interpolator type is defined using the full image type and the type
  //  used for representing space coordinates.
  //
  //  \index{itk::Nearest\-Neighbor\-Interpolate\-Image\-Function!instantiation}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  using InterpolatorType =
    itk::NearestNeighborInterpolateImageFunction<InputImageType, double>;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  An instance of the interpolator object is instantiated and passed to
  //  the resample filter.
  //
  //  \index{itk::ResampleImageFilter!SetInterpolator()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  InterpolatorType::Pointer interpolator = InterpolatorType::New();
  filter->SetInterpolator(interpolator);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  Given that some pixels of the output image may end up being mapped
  //  outside the extent of the input image it is necessary to decide what
  //  values to assign to them. This is done by invoking the
  //  \code{SetDefaultPixelValue()} method.
  //
  //  \index{itk::ResampleImageFilter!SetDefaultPixelValue()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetDefaultPixelValue(0);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The sampling grid of the output space is specified with the spacing
  //  along each dimension and the origin.
  //
  //  \index{itk::ResampleImageFilter!SetOutputOrigin()}
  //  \index{itk::ResampleImageFilter!SetOutputSpacing()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  // pixel spacing in millimeters along X and Y
  const double spacing[Dimension] = { 1.0, 1.0 };
  filter->SetOutputSpacing(spacing);

  // Physical space coordinate of origin for X and Y
  const double origin[Dimension] = { 0.0, 0.0 };
  filter->SetOutputOrigin(origin);
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginCodeSnippet
  InputImageType::DirectionType direction;
  direction.SetIdentity();
  filter->SetOutputDirection(direction);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The extent of the sampling grid on the output image is defined by a
  //  \code{SizeType} and is set using the \code{SetSize()} method.
  //
  //  \index{itk::ResampleImageFilter!SetSize()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  InputImageType::SizeType size;

  size[0] = 300; // number of pixels along X
  size[1] = 300; // number of pixels along Y

  filter->SetSize(size);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The input to the filter can be taken from any other filter, for example
  //  a reader. The output can be passed down the pipeline to other filters,
  //  for example a writer. An update call on any downstream filter will
  //  trigger the execution of the resampling filter.
  //
  //  \index{itk::ResampleImageFilter!SetInput()}
  //  \index{itk::ResampleImageFilter!GetOutput()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetInput(reader->GetOutput());
  writer->SetInput(filter->GetOutput());
  writer->Update();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=0.44\textwidth]{BrainProtonDensitySlice}
  // \includegraphics[width=0.44\textwidth]{ResampleImageFilterOutput1}
  // \itkcaption[Effect of the Resample filter]{Effect of the resample
  // filter.} \label{fig:ResampleImageFilterOutput1} \end{figure}
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=\textwidth]{ResampleImageFilterOutput1Analysis}
  // \itkcaption[Analysis of resampling in common coordinate system]{Analysis
  // of the resample image done in a common coordinate system.}
  // \label{fig:ResampleImageFilterOutput1Analysis}
  // \end{figure}
  //
  //  Figure \ref{fig:ResampleImageFilterOutput1} illustrates the effect of
  //  this filter on a slice of MRI brain image using an affine transform
  //  containing an identity transform. Note that any analysis of the
  //  behavior of this filter must be done on the space coordinate system in
  //  millimeters, not with respect to the sampling grid in pixels. The
  //  figure shows the resulting image in the lower left quarter of the
  //  extent. This may seem odd if analyzed in terms of the image grid but is
  //  quite clear when seen with respect to space coordinates.  Figure
  //  \ref{fig:ResampleImageFilterOutput1} is particularly misleading
  //  because the images are rescaled to fit nicely on the text of this book.
  //  Figure \ref{fig:ResampleImageFilterOutput1Analysis} clarifies the
  //  situation. It shows the two same images placed on an equally-scaled
  //  coordinate system.  It becomes clear here that an identity transform is
  //  being used to map the image data, and that simply, we have requested to
  //  resample additional empty space around the image. The input image is
  //  $181 \times 217$ pixels in size and we have requested an output of $300
  //  \times 300$ pixels. In this case, the input and output images both have
  //  spacing of $1mm \times 1mm$ and origin of $(0.0,0.0)$.
  //
  //  Software Guide : EndLatex

  //  Software Guide : BeginLatex
  //
  //  Let's now set values on the transform. Note that the supplied transform
  //  represents the mapping of points from the output space to the input
  //  space.  The following code sets up a translation.
  //
  //  \index{itk::AffineTransform!Translate()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  TransformType::OutputVectorType translation;
  translation[0] = -30; // X translation in millimeters
  translation[1] = -50; // Y translation in millimeters
  transform->Translate(translation);
  // Software Guide : EndCodeSnippet

  if (exampleAction == 1)
  {
    writer->Update();
  }

  //  Software Guide : BeginLatex
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=0.44\textwidth]{BrainProtonDensitySlice}
  // \includegraphics[width=0.44\textwidth]{ResampleImageFilterOutput2}
  // \itkcaption[ResampleImageFilter with a translation by
  // $(-30,-50)$]{ResampleImageFilter with a translation by $(-30,-50)$.}
  // \label{fig:ResampleImageFilterOutput2}
  // \end{figure}
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=\textwidth]{ResampleImageFilterOutput2Analysis}
  // \itkcaption[ResampleImageFilter. Analysis of a translation by
  // $(-30,-50)$]{ResampleImageFilter. Analysis of a translation by
  // $(-30,-50)$.}
  // \label{fig:ResampleImageFilterOutput2Analysis}
  // \end{figure}
  //
  // The output image resulting from the translation can be seen in Figure
  // \ref{fig:ResampleImageFilterOutput2}. Again, it is better to interpret
  // the result in a common coordinate system as illustrated in Figure
  // \ref{fig:ResampleImageFilterOutput2Analysis}.
  //
  // Probably the most important thing to keep in mind when resampling images
  // is that the transform is used to map points from the \textbf{output}
  // image space into the \textbf{input} image space. In this case, Figure
  // \ref{fig:ResampleImageFilterOutput2Analysis} shows that the translation
  // is applied to every point of the output image and the resulting position
  // is used to read the intensity from the input image. In this way, the
  // gray level of the point $P$ in the output image is taken from the point
  // $T(P)$ in the input image. Where $T$ is the transformation. In the
  // specific case of the Figure
  // \ref{fig:ResampleImageFilterOutput2Analysis}, the value of point
  // $(105,188)$ in the output image is taken from the point $(75,138)$ of
  // the input image because the transformation applied was a translation of
  // $(-30,-50)$.
  //
  //  Software Guide : EndLatex

  //  Software Guide : BeginLatex
  //
  //  It is sometimes useful to intentionally set the default output value to
  //  a distinct gray value in order to highlight the mapping of the image
  //  borders. For example, the following code sets the default external
  //  value of $100$.  The result is shown in the right side of Figure
  //  \ref{fig:ResampleImageFilterOutput3Analysis}.
  //
  //  \index{itk::ResampleImageFilter!SetDefaultPixelValue()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetDefaultPixelValue(100);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=\textwidth]{ResampleImageFilterOutput3Analysis}
  // \itkcaption[ResampleImageFilter highlighting image
  // borders]{ResampleImageFilter highlighting image borders with
  // SetDefaultPixelValue().}
  // \label{fig:ResampleImageFilterOutput3Analysis}
  // \end{figure}
  //
  //  With this change we can better appreciate the effect of the previous
  //  translation transform on the image resampling. Figure
  //  \ref{fig:ResampleImageFilterOutput3Analysis} illustrates how the point
  //  $(30,50)$ of the output image gets its gray value from the point $(0,0)$
  //  of the input image.
  //
  //  Software Guide : EndLatex

  if (exampleAction == 2)
  {
    writer->Update();
  }

  return EXIT_SUCCESS;
}
<commit_msg>BUG: Fix wrong argument count comparison in ResampleImageFilter example<commit_after>/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/

//  Software Guide : BeginCommandLineArgs
//    INPUTS:  {BrainProtonDensitySlice.png}
//    OUTPUTS: {ResampleImageFilterOutput1.png}
//    ARGUMENTS:    0
//  Software Guide : EndCommandLineArgs
//  Software Guide : BeginCommandLineArgs
//    INPUTS:  {BrainProtonDensitySlice.png}
//    OUTPUTS: {ResampleImageFilterOutput2.png}
//    ARGUMENTS:    1
//  Software Guide : EndCommandLineArgs
//  Software Guide : BeginCommandLineArgs
//    INPUTS:  {BrainProtonDensitySlice.png}
//    OUTPUTS: {ResampleImageFilterOutput3.png}
//    ARGUMENTS:    2
//  Software Guide : EndCommandLineArgs
//  Software Guide : BeginCommandLineArgs
//    INPUTS:  {BrainProtonDensitySlice.png}
//    OUTPUTS: {ResampleImageFilterOutput4.png}
//    ARGUMENTS:    3
//  Software Guide : EndCommandLineArgs
//
//  Software Guide : BeginLatex
//
//  Resampling an image is a very important task in image analysis. It is
//  especially important in the frame of image registration. The
//  \doxygen{ResampleImageFilter} implements image resampling through the use
//  of \doxygen{Transform}s. The inputs expected by this filter are an image,
//  a transform and an interpolator. The space coordinates of the image are
//  mapped through the transform in order to generate a new image. The extent
//  and spacing of the resulting image are selected by the user. Resampling
//  is performed in space coordinates, not pixel/grid coordinates. It is
//  quite important to ensure that image spacing is properly set on the
//  images involved. The interpolator is required since the mapping from one
//  space to the other will often require evaluation of the intensity of the
//  image at non-grid positions.
//
//  \index{itk::ResampleImageFilter}
//
//  Software Guide : EndLatex

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"

//  Software Guide : BeginLatex
//
//  The header file corresponding to this filter should be included first.
//
//  \index{itk::ResampleImageFilter!header}
//
//  Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkResampleImageFilter.h"
// Software Guide : EndCodeSnippet

//  Software Guide : BeginLatex
//
//  The header files corresponding to the transform and interpolator must
//  also be included.
//
//  \index{itk::AffineTransform!header}
//  \index{itk::Nearest\-Neighbor\-Interpolate\-Image\-Function!header}
//
//  Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkAffineTransform.h"
#include "itkNearestNeighborInterpolateImageFunction.h"
// Software Guide : EndCodeSnippet

int
main(int argc, char * argv[])
{
  if (argc < 3)
  {
    std::cerr << "Usage: " << std::endl;
    std::cerr << argv[0] << "  inputImageFile  outputImageFile";
    std::cerr << "  [exampleAction={0,1,2,3}]" << std::endl;
    return EXIT_FAILURE;
  }

  int exampleAction = 0;

  if (argc >= 4)
  {
    exampleAction = std::stoi(argv[3]);
  }

  //  Software Guide : BeginLatex
  //
  //  The dimension and pixel types for input and output image must be
  //  defined and with them the image types can be instantiated.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  constexpr unsigned int Dimension = 2;
  using InputPixelType = unsigned char;
  using OutputPixelType = unsigned char;
  using InputImageType = itk::Image<InputPixelType, Dimension>;
  using OutputImageType = itk::Image<OutputPixelType, Dimension>;
  // Software Guide : EndCodeSnippet

  using ReaderType = itk::ImageFileReader<InputImageType>;
  using WriterType = itk::ImageFileWriter<OutputImageType>;

  ReaderType::Pointer reader = ReaderType::New();
  WriterType::Pointer writer = WriterType::New();

  reader->SetFileName(argv[1]);
  writer->SetFileName(argv[2]);

  //  Software Guide : BeginLatex
  //
  //  Using the image and transform types it is now possible to instantiate
  //  the filter type and create the filter object.
  //
  //  \index{itk::ResampleImageFilter!instantiation}
  //  \index{itk::ResampleImageFilter!New()}
  //  \index{itk::ResampleImageFilter!Pointer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  using FilterType =
    itk::ResampleImageFilter<InputImageType, OutputImageType>;
  FilterType::Pointer filter = FilterType::New();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The transform type is typically defined using the image dimension
  //  and the type used for representing space coordinates.
  //
  //  \index{itk::AffineTransform!instantiation}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  using TransformType = itk::AffineTransform<double, Dimension>;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  An instance of the transform object is instantiated and passed to the
  //  resample filter. By default, the parameters of the transform are set to
  //  represent the identity transform.
  //
  //  \index{itk::ResampleImageFilter!SetTransform()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  TransformType::Pointer transform = TransformType::New();
  filter->SetTransform(transform);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The interpolator type is defined using the full image type and the type
  //  used for representing space coordinates.
  //
  //  \index{itk::Nearest\-Neighbor\-Interpolate\-Image\-Function!instantiation}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  using InterpolatorType =
    itk::NearestNeighborInterpolateImageFunction<InputImageType, double>;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  An instance of the interpolator object is instantiated and passed to
  //  the resample filter.
  //
  //  \index{itk::ResampleImageFilter!SetInterpolator()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  InterpolatorType::Pointer interpolator = InterpolatorType::New();
  filter->SetInterpolator(interpolator);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  Given that some pixels of the output image may end up being mapped
  //  outside the extent of the input image it is necessary to decide what
  //  values to assign to them. This is done by invoking the
  //  \code{SetDefaultPixelValue()} method.
  //
  //  \index{itk::ResampleImageFilter!SetDefaultPixelValue()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetDefaultPixelValue(0);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The sampling grid of the output space is specified with the spacing
  //  along each dimension and the origin.
  //
  //  \index{itk::ResampleImageFilter!SetOutputOrigin()}
  //  \index{itk::ResampleImageFilter!SetOutputSpacing()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  // pixel spacing in millimeters along X and Y
  const double spacing[Dimension] = { 1.0, 1.0 };
  filter->SetOutputSpacing(spacing);

  // Physical space coordinate of origin for X and Y
  const double origin[Dimension] = { 0.0, 0.0 };
  filter->SetOutputOrigin(origin);
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginCodeSnippet
  InputImageType::DirectionType direction;
  direction.SetIdentity();
  filter->SetOutputDirection(direction);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The extent of the sampling grid on the output image is defined by a
  //  \code{SizeType} and is set using the \code{SetSize()} method.
  //
  //  \index{itk::ResampleImageFilter!SetSize()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  InputImageType::SizeType size;

  size[0] = 300; // number of pixels along X
  size[1] = 300; // number of pixels along Y

  filter->SetSize(size);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The input to the filter can be taken from any other filter, for example
  //  a reader. The output can be passed down the pipeline to other filters,
  //  for example a writer. An update call on any downstream filter will
  //  trigger the execution of the resampling filter.
  //
  //  \index{itk::ResampleImageFilter!SetInput()}
  //  \index{itk::ResampleImageFilter!GetOutput()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetInput(reader->GetOutput());
  writer->SetInput(filter->GetOutput());
  writer->Update();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=0.44\textwidth]{BrainProtonDensitySlice}
  // \includegraphics[width=0.44\textwidth]{ResampleImageFilterOutput1}
  // \itkcaption[Effect of the Resample filter]{Effect of the resample
  // filter.} \label{fig:ResampleImageFilterOutput1} \end{figure}
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=\textwidth]{ResampleImageFilterOutput1Analysis}
  // \itkcaption[Analysis of resampling in common coordinate system]{Analysis
  // of the resample image done in a common coordinate system.}
  // \label{fig:ResampleImageFilterOutput1Analysis}
  // \end{figure}
  //
  //  Figure \ref{fig:ResampleImageFilterOutput1} illustrates the effect of
  //  this filter on a slice of MRI brain image using an affine transform
  //  containing an identity transform. Note that any analysis of the
  //  behavior of this filter must be done on the space coordinate system in
  //  millimeters, not with respect to the sampling grid in pixels. The
  //  figure shows the resulting image in the lower left quarter of the
  //  extent. This may seem odd if analyzed in terms of the image grid but is
  //  quite clear when seen with respect to space coordinates.  Figure
  //  \ref{fig:ResampleImageFilterOutput1} is particularly misleading
  //  because the images are rescaled to fit nicely on the text of this book.
  //  Figure \ref{fig:ResampleImageFilterOutput1Analysis} clarifies the
  //  situation. It shows the two same images placed on an equally-scaled
  //  coordinate system.  It becomes clear here that an identity transform is
  //  being used to map the image data, and that simply, we have requested to
  //  resample additional empty space around the image. The input image is
  //  $181 \times 217$ pixels in size and we have requested an output of $300
  //  \times 300$ pixels. In this case, the input and output images both have
  //  spacing of $1mm \times 1mm$ and origin of $(0.0,0.0)$.
  //
  //  Software Guide : EndLatex

  //  Software Guide : BeginLatex
  //
  //  Let's now set values on the transform. Note that the supplied transform
  //  represents the mapping of points from the output space to the input
  //  space.  The following code sets up a translation.
  //
  //  \index{itk::AffineTransform!Translate()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  TransformType::OutputVectorType translation;
  translation[0] = -30; // X translation in millimeters
  translation[1] = -50; // Y translation in millimeters
  transform->Translate(translation);
  // Software Guide : EndCodeSnippet

  if (exampleAction == 1)
  {
    writer->Update();
  }

  //  Software Guide : BeginLatex
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=0.44\textwidth]{BrainProtonDensitySlice}
  // \includegraphics[width=0.44\textwidth]{ResampleImageFilterOutput2}
  // \itkcaption[ResampleImageFilter with a translation by
  // $(-30,-50)$]{ResampleImageFilter with a translation by $(-30,-50)$.}
  // \label{fig:ResampleImageFilterOutput2}
  // \end{figure}
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=\textwidth]{ResampleImageFilterOutput2Analysis}
  // \itkcaption[ResampleImageFilter. Analysis of a translation by
  // $(-30,-50)$]{ResampleImageFilter. Analysis of a translation by
  // $(-30,-50)$.}
  // \label{fig:ResampleImageFilterOutput2Analysis}
  // \end{figure}
  //
  // The output image resulting from the translation can be seen in Figure
  // \ref{fig:ResampleImageFilterOutput2}. Again, it is better to interpret
  // the result in a common coordinate system as illustrated in Figure
  // \ref{fig:ResampleImageFilterOutput2Analysis}.
  //
  // Probably the most important thing to keep in mind when resampling images
  // is that the transform is used to map points from the \textbf{output}
  // image space into the \textbf{input} image space. In this case, Figure
  // \ref{fig:ResampleImageFilterOutput2Analysis} shows that the translation
  // is applied to every point of the output image and the resulting position
  // is used to read the intensity from the input image. In this way, the
  // gray level of the point $P$ in the output image is taken from the point
  // $T(P)$ in the input image. Where $T$ is the transformation. In the
  // specific case of the Figure
  // \ref{fig:ResampleImageFilterOutput2Analysis}, the value of point
  // $(105,188)$ in the output image is taken from the point $(75,138)$ of
  // the input image because the transformation applied was a translation of
  // $(-30,-50)$.
  //
  //  Software Guide : EndLatex

  //  Software Guide : BeginLatex
  //
  //  It is sometimes useful to intentionally set the default output value to
  //  a distinct gray value in order to highlight the mapping of the image
  //  borders. For example, the following code sets the default external
  //  value of $100$.  The result is shown in the right side of Figure
  //  \ref{fig:ResampleImageFilterOutput3Analysis}.
  //
  //  \index{itk::ResampleImageFilter!SetDefaultPixelValue()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetDefaultPixelValue(100);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=\textwidth]{ResampleImageFilterOutput3Analysis}
  // \itkcaption[ResampleImageFilter highlighting image
  // borders]{ResampleImageFilter highlighting image borders with
  // SetDefaultPixelValue().}
  // \label{fig:ResampleImageFilterOutput3Analysis}
  // \end{figure}
  //
  //  With this change we can better appreciate the effect of the previous
  //  translation transform on the image resampling. Figure
  //  \ref{fig:ResampleImageFilterOutput3Analysis} illustrates how the point
  //  $(30,50)$ of the output image gets its gray value from the point $(0,0)$
  //  of the input image.
  //
  //  Software Guide : EndLatex

  if (exampleAction == 2)
  {
    writer->Update();
  }

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2017 gRPC authors.
 *
 * 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 <string.h>

#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>

#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
#include "src/core/ext/filters/client_channel/parse_address.h"
#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
#include "src/core/ext/filters/client_channel/resolver_registry.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/security/credentials/fake/fake_credentials.h"

#include "test/core/util/test_config.h"

static grpc_core::OrphanablePtr<grpc_core::Resolver> build_fake_resolver(
    grpc_combiner* combiner,
    grpc_core::FakeResolverResponseGenerator* response_generator) {
  grpc_core::ResolverFactory* factory =
      grpc_core::ResolverRegistry::LookupResolverFactory("fake");
  grpc_arg generator_arg =
      grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
          response_generator);
  grpc_channel_args channel_args = {1, &generator_arg};
  grpc_core::ResolverArgs args;
  args.args = &channel_args;
  args.combiner = combiner;
  grpc_core::OrphanablePtr<grpc_core::Resolver> resolver =
      factory->CreateResolver(args);
  return resolver;
}

typedef struct on_resolution_arg {
  grpc_channel_args* resolver_result;
  grpc_channel_args* expected_resolver_result;
  gpr_event ev;
} on_resolution_arg;

// Callback to check the resolution result is as expected.
void on_resolution_cb(void* arg, grpc_error* error) {
  if (error != GRPC_ERROR_NONE) return;
  on_resolution_arg* res = static_cast<on_resolution_arg*>(arg);
  // We only check the addresses channel arg because that's the only one
  // explicitly set by the test via
  // FakeResolverResponseGenerator::SetResponse().
  const grpc_lb_addresses* actual_lb_addresses =
      grpc_lb_addresses_find_channel_arg(res->resolver_result);
  const grpc_lb_addresses* expected_lb_addresses =
      grpc_lb_addresses_find_channel_arg(res->expected_resolver_result);
  GPR_ASSERT(
      grpc_lb_addresses_cmp(actual_lb_addresses, expected_lb_addresses) == 0);
  grpc_channel_args_destroy(res->resolver_result);
  grpc_channel_args_destroy(res->expected_resolver_result);
  gpr_event_set(&res->ev, (void*)1);
}

// Create a new resolution containing 2 addresses.
static grpc_channel_args* create_new_resolver_result() {
  static size_t test_counter = 0;
  const size_t num_addresses = 2;
  char* uri_string;
  char* balancer_name;
  // Create grpc_lb_addresses.
  grpc_lb_addresses* addresses =
      grpc_lb_addresses_create(num_addresses, nullptr);
  for (size_t i = 0; i < num_addresses; ++i) {
    gpr_asprintf(&uri_string, "ipv4:127.0.0.1:100%" PRIuPTR,
                 test_counter * num_addresses + i);
    grpc_uri* uri = grpc_uri_parse(uri_string, true);
    gpr_asprintf(&balancer_name, "balancer%" PRIuPTR,
                 test_counter * num_addresses + i);
    grpc_lb_addresses_set_address_from_uri(
        addresses, i, uri, bool(num_addresses % 2), balancer_name, nullptr);
    gpr_free(balancer_name);
    grpc_uri_destroy(uri);
    gpr_free(uri_string);
  }
  // Convert grpc_lb_addresses to grpc_channel_args.
  const grpc_arg addresses_arg =
      grpc_lb_addresses_create_channel_arg(addresses);
  grpc_channel_args* results =
      grpc_channel_args_copy_and_add(nullptr, &addresses_arg, 1);
  grpc_lb_addresses_destroy(addresses);
  ++test_counter;
  return results;
}

static on_resolution_arg create_on_resolution_arg(grpc_channel_args* results) {
  on_resolution_arg on_res_arg;
  memset(&on_res_arg, 0, sizeof(on_res_arg));
  on_res_arg.expected_resolver_result = results;
  gpr_event_init(&on_res_arg.ev);
  return on_res_arg;
}

static void test_fake_resolver() {
  grpc_core::ExecCtx exec_ctx;
  grpc_combiner* combiner = grpc_combiner_create();
  // Create resolver.
  grpc_core::RefCountedPtr<grpc_core::FakeResolverResponseGenerator>
      response_generator =
          grpc_core::MakeRefCounted<grpc_core::FakeResolverResponseGenerator>();
  grpc_core::OrphanablePtr<grpc_core::Resolver> resolver =
      build_fake_resolver(combiner, response_generator.get());
  GPR_ASSERT(resolver.get() != nullptr);
  // Test 1: normal resolution.
  // next_results != NULL, reresolution_results == NULL, last_used_results ==
  // NULL. Expected response is next_results.
  grpc_channel_args* results = create_new_resolver_result();
  on_resolution_arg on_res_arg = create_on_resolution_arg(results);
  grpc_closure* on_resolution = GRPC_CLOSURE_CREATE(
      on_resolution_cb, &on_res_arg, grpc_combiner_scheduler(combiner));
  // Resolution won't be triggered until next_results is set.
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  response_generator->SetResponse(results);
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 2: update resolution.
  // next_results != NULL, reresolution_results == NULL, last_used_results !=
  // NULL. Expected response is next_results.
  results = create_new_resolver_result();
  grpc_channel_args* last_used_results = grpc_channel_args_copy(results);
  on_res_arg = create_on_resolution_arg(results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  // Resolution won't be triggered until next_results is set.
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  response_generator->SetResponse(results);
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 3: fallback re-resolution.
  // next_results == NULL, reresolution_results == NULL, last_used_results !=
  // NULL. Expected response is last_used_results.
  on_res_arg = create_on_resolution_arg(last_used_results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  // Trigger a re-resolution.
  resolver->RequestReresolutionLocked();
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 4: normal re-resolution.
  // next_results == NULL, reresolution_results != NULL, last_used_results !=
  // NULL. Expected response is reresolution_results.
  grpc_channel_args* reresolution_results = create_new_resolver_result();
  on_res_arg =
      create_on_resolution_arg(grpc_channel_args_copy(reresolution_results));
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  // Set reresolution_results.
  response_generator->SetReresolutionResponse(reresolution_results);
  // Flush here to guarantee that the response has been set.
  grpc_core::ExecCtx::Get()->Flush();
  // Trigger a re-resolution.
  resolver->RequestReresolutionLocked();
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 5: repeat re-resolution.
  // next_results == NULL, reresolution_results != NULL, last_used_results !=
  // NULL. Expected response is reresolution_results.
  on_res_arg = create_on_resolution_arg(reresolution_results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  // Trigger a re-resolution.
  resolver->RequestReresolutionLocked();
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 6: normal resolution.
  // next_results != NULL, reresolution_results != NULL, last_used_results !=
  // NULL. Expected response is next_results.
  results = create_new_resolver_result();
  last_used_results = grpc_channel_args_copy(results);
  on_res_arg = create_on_resolution_arg(results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  // Resolution won't be triggered until next_results is set.
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  response_generator->SetResponse(results);
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 7: fallback re-resolution.
  // next_results == NULL, reresolution_results == NULL, last_used_results !=
  // NULL. Expected response is last_used_results.
  on_res_arg = create_on_resolution_arg(last_used_results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  // Reset reresolution_results.
  response_generator->SetReresolutionResponse(nullptr);
  // Flush here to guarantee that reresolution_results has been reset.
  grpc_core::ExecCtx::Get()->Flush();
  // Trigger a re-resolution.
  resolver->RequestReresolutionLocked();
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 8: no-op.
  // Requesting a new resolution without setting the response shouldn't trigger
  // the resolution callback.
  memset(&on_res_arg, 0, sizeof(on_res_arg));
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_milliseconds_to_deadline(100)) ==
             nullptr);
  // Clean up.
  GRPC_COMBINER_UNREF(combiner, "test_fake_resolver");
}

int main(int argc, char** argv) {
  grpc_test_init(argc, argv);
  grpc_init();

  test_fake_resolver();

  grpc_shutdown();
  return 0;
}
<commit_msg>Fix asan failure in fake_resolver_test.<commit_after>/*
 *
 * Copyright 2017 gRPC authors.
 *
 * 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 <string.h>

#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>

#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
#include "src/core/ext/filters/client_channel/parse_address.h"
#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
#include "src/core/ext/filters/client_channel/resolver_registry.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/security/credentials/fake/fake_credentials.h"

#include "test/core/util/test_config.h"

static grpc_core::OrphanablePtr<grpc_core::Resolver> build_fake_resolver(
    grpc_combiner* combiner,
    grpc_core::FakeResolverResponseGenerator* response_generator) {
  grpc_core::ResolverFactory* factory =
      grpc_core::ResolverRegistry::LookupResolverFactory("fake");
  grpc_arg generator_arg =
      grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
          response_generator);
  grpc_channel_args channel_args = {1, &generator_arg};
  grpc_core::ResolverArgs args;
  args.args = &channel_args;
  args.combiner = combiner;
  grpc_core::OrphanablePtr<grpc_core::Resolver> resolver =
      factory->CreateResolver(args);
  return resolver;
}

typedef struct on_resolution_arg {
  grpc_channel_args* resolver_result;
  grpc_channel_args* expected_resolver_result;
  gpr_event ev;
} on_resolution_arg;

// Callback to check the resolution result is as expected.
void on_resolution_cb(void* arg, grpc_error* error) {
  if (error != GRPC_ERROR_NONE) return;
  on_resolution_arg* res = static_cast<on_resolution_arg*>(arg);
  // We only check the addresses channel arg because that's the only one
  // explicitly set by the test via
  // FakeResolverResponseGenerator::SetResponse().
  const grpc_lb_addresses* actual_lb_addresses =
      grpc_lb_addresses_find_channel_arg(res->resolver_result);
  const grpc_lb_addresses* expected_lb_addresses =
      grpc_lb_addresses_find_channel_arg(res->expected_resolver_result);
  GPR_ASSERT(
      grpc_lb_addresses_cmp(actual_lb_addresses, expected_lb_addresses) == 0);
  grpc_channel_args_destroy(res->resolver_result);
  grpc_channel_args_destroy(res->expected_resolver_result);
  gpr_event_set(&res->ev, (void*)1);
}

// Create a new resolution containing 2 addresses.
static grpc_channel_args* create_new_resolver_result() {
  static size_t test_counter = 0;
  const size_t num_addresses = 2;
  char* uri_string;
  char* balancer_name;
  // Create grpc_lb_addresses.
  grpc_lb_addresses* addresses =
      grpc_lb_addresses_create(num_addresses, nullptr);
  for (size_t i = 0; i < num_addresses; ++i) {
    gpr_asprintf(&uri_string, "ipv4:127.0.0.1:100%" PRIuPTR,
                 test_counter * num_addresses + i);
    grpc_uri* uri = grpc_uri_parse(uri_string, true);
    gpr_asprintf(&balancer_name, "balancer%" PRIuPTR,
                 test_counter * num_addresses + i);
    grpc_lb_addresses_set_address_from_uri(
        addresses, i, uri, bool(num_addresses % 2), balancer_name, nullptr);
    gpr_free(balancer_name);
    grpc_uri_destroy(uri);
    gpr_free(uri_string);
  }
  // Convert grpc_lb_addresses to grpc_channel_args.
  const grpc_arg addresses_arg =
      grpc_lb_addresses_create_channel_arg(addresses);
  grpc_channel_args* results =
      grpc_channel_args_copy_and_add(nullptr, &addresses_arg, 1);
  grpc_lb_addresses_destroy(addresses);
  ++test_counter;
  return results;
}

static on_resolution_arg create_on_resolution_arg(grpc_channel_args* results) {
  on_resolution_arg on_res_arg;
  memset(&on_res_arg, 0, sizeof(on_res_arg));
  on_res_arg.expected_resolver_result = results;
  gpr_event_init(&on_res_arg.ev);
  return on_res_arg;
}

static void test_fake_resolver() {
  grpc_core::ExecCtx exec_ctx;
  grpc_combiner* combiner = grpc_combiner_create();
  // Create resolver.
  grpc_core::RefCountedPtr<grpc_core::FakeResolverResponseGenerator>
      response_generator =
          grpc_core::MakeRefCounted<grpc_core::FakeResolverResponseGenerator>();
  grpc_core::OrphanablePtr<grpc_core::Resolver> resolver =
      build_fake_resolver(combiner, response_generator.get());
  GPR_ASSERT(resolver.get() != nullptr);
  // Test 1: normal resolution.
  // next_results != NULL, reresolution_results == NULL, last_used_results ==
  // NULL. Expected response is next_results.
  grpc_channel_args* results = create_new_resolver_result();
  on_resolution_arg on_res_arg = create_on_resolution_arg(results);
  grpc_closure* on_resolution = GRPC_CLOSURE_CREATE(
      on_resolution_cb, &on_res_arg, grpc_combiner_scheduler(combiner));
  // Resolution won't be triggered until next_results is set.
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  response_generator->SetResponse(results);
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 2: update resolution.
  // next_results != NULL, reresolution_results == NULL, last_used_results !=
  // NULL. Expected response is next_results.
  results = create_new_resolver_result();
  grpc_channel_args* last_used_results = grpc_channel_args_copy(results);
  on_res_arg = create_on_resolution_arg(results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  // Resolution won't be triggered until next_results is set.
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  response_generator->SetResponse(results);
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 3: fallback re-resolution.
  // next_results == NULL, reresolution_results == NULL, last_used_results !=
  // NULL. Expected response is last_used_results.
  on_res_arg = create_on_resolution_arg(last_used_results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  // Trigger a re-resolution.
  resolver->RequestReresolutionLocked();
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 4: normal re-resolution.
  // next_results == NULL, reresolution_results != NULL, last_used_results !=
  // NULL. Expected response is reresolution_results.
  grpc_channel_args* reresolution_results = create_new_resolver_result();
  on_res_arg =
      create_on_resolution_arg(grpc_channel_args_copy(reresolution_results));
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  // Set reresolution_results.
  response_generator->SetReresolutionResponse(reresolution_results);
  // Flush here to guarantee that the response has been set.
  grpc_core::ExecCtx::Get()->Flush();
  // Trigger a re-resolution.
  resolver->RequestReresolutionLocked();
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 5: repeat re-resolution.
  // next_results == NULL, reresolution_results != NULL, last_used_results !=
  // NULL. Expected response is reresolution_results.
  on_res_arg = create_on_resolution_arg(reresolution_results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  // Trigger a re-resolution.
  resolver->RequestReresolutionLocked();
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 6: normal resolution.
  // next_results != NULL, reresolution_results != NULL, last_used_results !=
  // NULL. Expected response is next_results.
  results = create_new_resolver_result();
  last_used_results = grpc_channel_args_copy(results);
  on_res_arg = create_on_resolution_arg(results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  // Resolution won't be triggered until next_results is set.
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  response_generator->SetResponse(results);
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 7: fallback re-resolution.
  // next_results == NULL, reresolution_results == NULL, last_used_results !=
  // NULL. Expected response is last_used_results.
  on_res_arg = create_on_resolution_arg(last_used_results);
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  // Reset reresolution_results.
  response_generator->SetReresolutionResponse(nullptr);
  // Flush here to guarantee that reresolution_results has been reset.
  grpc_core::ExecCtx::Get()->Flush();
  // Trigger a re-resolution.
  resolver->RequestReresolutionLocked();
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_seconds_to_deadline(5)) != nullptr);
  // Test 8: no-op.
  // Requesting a new resolution without setting the response shouldn't trigger
  // the resolution callback.
  memset(&on_res_arg, 0, sizeof(on_res_arg));
  on_resolution = GRPC_CLOSURE_CREATE(on_resolution_cb, &on_res_arg,
                                      grpc_combiner_scheduler(combiner));
  resolver->NextLocked(&on_res_arg.resolver_result, on_resolution);
  grpc_core::ExecCtx::Get()->Flush();
  GPR_ASSERT(gpr_event_wait(&on_res_arg.ev,
                            grpc_timeout_milliseconds_to_deadline(100)) ==
             nullptr);
  // Clean up.
  // Note: Need to explicitly unref the resolver and flush the exec_ctx
  // to make sure that the final resolver callback (with error set to
  // "Resolver Shutdown") is invoked before on_res_arg goes out of scope.
  resolver.reset();
  grpc_core::ExecCtx::Get()->Flush();
  GRPC_COMBINER_UNREF(combiner, "test_fake_resolver");
}

int main(int argc, char** argv) {
  grpc_test_init(argc, argv);
  grpc_init();

  test_fake_resolver();

  grpc_shutdown();
  return 0;
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

// mapnik
#include <mapnik/feature.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/json/topology.hpp>
#include <mapnik/util/variant.hpp>
// stl
#include <string>
#include <vector>
#include <fstream>

// boost
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-local-typedef"
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/geometry.hpp>
#pragma GCC diagnostic pop

#include "topojson_featureset.hpp"

namespace mapnik { namespace topojson {

struct attribute_value_visitor

{
public:
    attribute_value_visitor(mapnik::transcoder const& tr)
        : tr_(tr) {}

    mapnik::value operator()(std::string const& val) const
    {
        return mapnik::value(tr_.transcode(val.c_str()));
    }

    template <typename T>
    mapnik::value operator()(T const& val) const
    {
        return mapnik::value(val);
    }

    mapnik::transcoder const& tr_;
};

template <typename T>
void assign_properties(mapnik::feature_impl & feature, T const& geom, mapnik::transcoder const& tr)
{
    if ( geom.props)
    {
        for (auto const& p : *geom.props)
        {
            feature.put_new(std::get<0>(p), mapnik::util::apply_visitor(attribute_value_visitor(tr),std::get<1>(p)));
        }
    }
}

template <typename Context>
struct feature_generator
{
    feature_generator(Context & ctx,  mapnik::transcoder const& tr, topology const& topo, std::size_t feature_id)
        : ctx_(ctx),
          tr_(tr),
          topo_(topo),
          feature_id_(feature_id) {}

    feature_ptr operator() (point const& pt) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        double x = pt.coord.x;
        double y = pt.coord.y;
        if (topo_.tr)
        {
            x =  x * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
            y =  y * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
        }
        mapnik::new_geometry::point point(x, y);
        feature->set_geometry(std::move(point));
        assign_properties(*feature, pt, tr_);
        return feature;
    }

    feature_ptr operator() (multi_point const& multi_pt) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        mapnik::new_geometry::multi_point multi_point;
        multi_point.reserve(multi_pt.points.size());
        for (auto const& pt : multi_pt.points)
        {
            double x = pt.x;
            double y = pt.y;
            if (topo_.tr)
            {
                x =  x * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
                y =  y * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
            }
            multi_point.add_coord(x, y);
        }
        feature->set_geometry(std::move(multi_point));
        assign_properties(*feature, multi_pt, tr_);
        return feature;
    }

    feature_ptr operator() (linestring const& line) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        double px = 0, py = 0;
        index_type arc_index = line.ring;

        mapnik::new_geometry::line_string line_string;
        line_string.reserve( topo_.arcs[arc_index].coordinates.size());

        for (auto pt : topo_.arcs[arc_index].coordinates)
        {
            double x = pt.x;
            double y = pt.y;
            if (topo_.tr)
            {
                x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
                y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
            }
            line_string.add_coord(x,y);
        }
        feature->set_geometry(std::move(line_string));
        assign_properties(*feature, line, tr_);
        return feature;
    }

    feature_ptr operator() (multi_linestring const& multi_line) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));

        mapnik::new_geometry::multi_line_string multi_line_string;
        multi_line_string.reserve(multi_line.rings.size());
        for (auto const& index : multi_line.rings)
        {
            double px = 0, py = 0;
            bool reverse = index < 0;
            index_type arc_index = reverse ? std::abs(index) - 1 : index;
            mapnik::new_geometry::line_string line_string;
            line_string.reserve(topo_.arcs[arc_index].coordinates.size());
            for (auto pt : topo_.arcs[arc_index].coordinates)
            {
                double x = pt.x;
                double y = pt.y;
                if (topo_.tr)
                {
                    x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
                    y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
                }
                line_string.add_coord(x, y);
            }
            multi_line_string.push_back(std::move(line_string));
        }
        feature->set_geometry(std::move(multi_line_string));
        assign_properties(*feature, multi_line, tr_);
        return feature;
    }

    feature_ptr operator() (polygon const& poly) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        std::vector<mapnik::topojson::coordinate> processed_coords;
        mapnik::new_geometry::polygon polygon;
        if (poly.rings.size() > 1) polygon.interior_rings.reserve(poly.rings.size() - 1);
        bool first = true;
        for (auto const& ring : poly.rings)
        {
            mapnik::new_geometry::linear_ring linear_ring;
            for (auto const& index : ring)
            {
                double px = 0, py = 0;
                bool reverse = index < 0;
                index_type arc_index = reverse ? std::abs(index) - 1 : index;
                auto const& coords = topo_.arcs[arc_index].coordinates;
                processed_coords.clear();
                processed_coords.reserve(coords.size());
                for (auto const& pt : coords )
                {
                    double x = pt.x;
                    double y = pt.y;

                    if (topo_.tr)
                    {
                        transform const& tr = *topo_.tr;
                        x =  (px += x) * tr.scale_x + tr.translate_x;
                        y =  (py += y) * tr.scale_y + tr.translate_y;
                    }
                    processed_coords.emplace_back(coordinate{x,y});
                }

                using namespace boost::adaptors;

                if (reverse)
                {
                    for (auto const& c : processed_coords | reversed | sliced(0, processed_coords.size()-1))
                    {
                        linear_ring.emplace_back(c.x, c.y);
                    }
                }
                else
                {
                    for (auto const& c : processed_coords | sliced(0, processed_coords.size()-1))
                    {
                        linear_ring.emplace_back(c.x, c.y);
                    }
                }
            }
            if (first)
            {
                first = false;
                polygon.set_exterior_ring(std::move(linear_ring));
            }
            else
            {
                polygon.add_hole(std::move(linear_ring));
            }
        }

        feature->set_geometry(std::move(polygon));
        assign_properties(*feature, poly, tr_);
        return feature;
    }

    feature_ptr operator() (multi_polygon const& multi_poly) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        std::vector<mapnik::topojson::coordinate> processed_coords;
        mapnik::new_geometry::multi_polygon multi_polygon;
        multi_polygon.reserve(multi_poly.polygons.size());

        for (auto const& poly : multi_poly.polygons)
        {
            //std::unique_ptr<geometry_type> poly_ptr(new geometry_type(geometry_type::types::Polygon));
            bool first = true;
            mapnik::new_geometry::polygon polygon;
            if (poly.size() > 1) polygon.interior_rings.reserve(poly.size() - 1);

            for (auto const& ring : poly)
            {
                mapnik::new_geometry::linear_ring linear_ring;
                for (auto const& index : ring)
                {
                    double px = 0, py = 0;
                    bool reverse = index < 0;
                    index_type arc_index = reverse ? std::abs(index) - 1 : index;
                    auto const& coords = topo_.arcs[arc_index].coordinates;
                    processed_coords.clear();
                    processed_coords.reserve(coords.size());
                    for (auto const& pt : coords )
                    {
                        double x = pt.x;
                        double y = pt.y;

                        if (topo_.tr)
                        {
                            x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
                            y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
                        }
                        processed_coords.emplace_back(coordinate{x,y});
                    }

                    using namespace boost::adaptors;

                    if (reverse)
                    {
                        for (auto const& c : (processed_coords | reversed | sliced(0,processed_coords.size() - 1)))
                        {
                            linear_ring.add_coord(c.x, c.y);
                        }
                    }
                    else
                    {
                        for (auto const& c : processed_coords | sliced(0,processed_coords.size() - 1))
                        {
                            linear_ring.add_coord(c.x, c.y);
                        }
                    }
                }
                if (first)
                {
                    first = false;
                    polygon.set_exterior_ring(std::move(linear_ring));
                }
                else
                {
                    polygon.add_hole(std::move(linear_ring));
                }
            }
            multi_polygon.push_back(std::move(polygon));
        }
        feature->set_geometry(std::move(multi_polygon));
        assign_properties(*feature, multi_poly, tr_);
        return feature;
    }

    template<typename T>
    feature_ptr operator() (T const& ) const
    {
        return feature_ptr();
    }

    Context & ctx_;
    mapnik::transcoder const& tr_;
    topology const& topo_;
    std::size_t feature_id_;
};

}}

topojson_featureset::topojson_featureset(mapnik::topojson::topology const& topo,
                                         mapnik::transcoder const& tr,
                                         array_type && index_array)
    : ctx_(std::make_shared<mapnik::context_type>()),
      topo_(topo),
      tr_(tr),
      index_array_(std::move(index_array)),
      index_itr_(index_array_.begin()),
      index_end_(index_array_.end()),
      feature_id_ (0) {}

topojson_featureset::~topojson_featureset() {}

mapnik::feature_ptr topojson_featureset::next()
{
    if (index_itr_ != index_end_)
    {
        topojson_datasource::item_type const& item = *index_itr_++;
        std::size_t index = item.second;
        if ( index < topo_.geometries.size())
        {
            mapnik::topojson::geometry const& geom = topo_.geometries[index];
            mapnik::feature_ptr feature = mapnik::util::apply_visitor(
                mapnik::topojson::feature_generator<mapnik::context_ptr>(ctx_, tr_, topo_, feature_id_++),
                geom);
            return feature;
        }
    }

    return mapnik::feature_ptr();
}
<commit_msg>remove slicing<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

// mapnik
#include <mapnik/feature.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/json/topology.hpp>
#include <mapnik/util/variant.hpp>
#include <mapnik/geometry_adapters.hpp>
// stl
#include <string>
#include <vector>
#include <fstream>

// boost
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-local-typedef"
#include <boost/range/adaptor/reversed.hpp>
#include <boost/geometry.hpp>
#pragma GCC diagnostic pop

#include "topojson_featureset.hpp"

namespace mapnik { namespace topojson {

struct attribute_value_visitor

{
public:
    attribute_value_visitor(mapnik::transcoder const& tr)
        : tr_(tr) {}

    mapnik::value operator()(std::string const& val) const
    {
        return mapnik::value(tr_.transcode(val.c_str()));
    }

    template <typename T>
    mapnik::value operator()(T const& val) const
    {
        return mapnik::value(val);
    }

    mapnik::transcoder const& tr_;
};

template <typename T>
void assign_properties(mapnik::feature_impl & feature, T const& geom, mapnik::transcoder const& tr)
{
    if ( geom.props)
    {
        for (auto const& p : *geom.props)
        {
            feature.put_new(std::get<0>(p), mapnik::util::apply_visitor(attribute_value_visitor(tr),std::get<1>(p)));
        }
    }
}

template <typename Context>
struct feature_generator
{
    feature_generator(Context & ctx,  mapnik::transcoder const& tr, topology const& topo, std::size_t feature_id)
        : ctx_(ctx),
          tr_(tr),
          topo_(topo),
          feature_id_(feature_id) {}

    feature_ptr operator() (point const& pt) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        double x = pt.coord.x;
        double y = pt.coord.y;
        if (topo_.tr)
        {
            x =  x * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
            y =  y * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
        }
        mapnik::new_geometry::point point(x, y);
        feature->set_geometry(std::move(point));
        assign_properties(*feature, pt, tr_);
        return feature;
    }

    feature_ptr operator() (multi_point const& multi_pt) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        mapnik::new_geometry::multi_point multi_point;
        multi_point.reserve(multi_pt.points.size());
        for (auto const& pt : multi_pt.points)
        {
            double x = pt.x;
            double y = pt.y;
            if (topo_.tr)
            {
                x =  x * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
                y =  y * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
            }
            multi_point.add_coord(x, y);
        }
        feature->set_geometry(std::move(multi_point));
        assign_properties(*feature, multi_pt, tr_);
        return feature;
    }

    feature_ptr operator() (linestring const& line) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        double px = 0, py = 0;
        index_type arc_index = line.ring;

        mapnik::new_geometry::line_string line_string;
        line_string.reserve( topo_.arcs[arc_index].coordinates.size());

        for (auto pt : topo_.arcs[arc_index].coordinates)
        {
            double x = pt.x;
            double y = pt.y;
            if (topo_.tr)
            {
                x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
                y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
            }
            line_string.add_coord(x,y);
        }
        feature->set_geometry(std::move(line_string));
        assign_properties(*feature, line, tr_);
        return feature;
    }

    feature_ptr operator() (multi_linestring const& multi_line) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));

        mapnik::new_geometry::multi_line_string multi_line_string;
        multi_line_string.reserve(multi_line.rings.size());
        for (auto const& index : multi_line.rings)
        {
            double px = 0, py = 0;
            bool reverse = index < 0;
            index_type arc_index = reverse ? std::abs(index) - 1 : index;
            mapnik::new_geometry::line_string line_string;
            line_string.reserve(topo_.arcs[arc_index].coordinates.size());
            for (auto pt : topo_.arcs[arc_index].coordinates)
            {
                double x = pt.x;
                double y = pt.y;
                if (topo_.tr)
                {
                    x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
                    y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
                }
                line_string.add_coord(x, y);
            }
            multi_line_string.push_back(std::move(line_string));
        }
        feature->set_geometry(std::move(multi_line_string));
        assign_properties(*feature, multi_line, tr_);
        return feature;
    }

    feature_ptr operator() (polygon const& poly) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        std::vector<mapnik::topojson::coordinate> processed_coords;
        mapnik::new_geometry::polygon polygon;
        if (poly.rings.size() > 1) polygon.interior_rings.reserve(poly.rings.size() - 1);
        bool first = true;
        for (auto const& ring : poly.rings)
        {
            mapnik::new_geometry::linear_ring linear_ring;
            for (auto const& index : ring)
            {
                double px = 0, py = 0;
                bool reverse = index < 0;
                index_type arc_index = reverse ? std::abs(index) - 1 : index;
                auto const& coords = topo_.arcs[arc_index].coordinates;
                processed_coords.clear();
                processed_coords.reserve(coords.size());
                for (auto const& pt : coords )
                {
                    double x = pt.x;
                    double y = pt.y;

                    if (topo_.tr)
                    {
                        transform const& tr = *topo_.tr;
                        x =  (px += x) * tr.scale_x + tr.translate_x;
                        y =  (py += y) * tr.scale_y + tr.translate_y;
                    }
                    processed_coords.emplace_back(coordinate{x,y});
                }

                using namespace boost::adaptors;

                if (reverse)
                {
                    for (auto const& c : processed_coords | reversed)
                    {
                        linear_ring.emplace_back(c.x, c.y);
                    }
                }
                else
                {
                    for (auto const& c : processed_coords)
                    {
                        linear_ring.emplace_back(c.x, c.y);
                    }
                }
            }
            if (first)
            {
                first = false;
                polygon.set_exterior_ring(std::move(linear_ring));
            }
            else
            {
                polygon.add_hole(std::move(linear_ring));
            }
        }
        boost::geometry::correct(polygon);
        feature->set_geometry(std::move(polygon));
        assign_properties(*feature, poly, tr_);
        return feature;
    }

    feature_ptr operator() (multi_polygon const& multi_poly) const
    {
        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
        std::vector<mapnik::topojson::coordinate> processed_coords;
        mapnik::new_geometry::multi_polygon multi_polygon;
        multi_polygon.reserve(multi_poly.polygons.size());

        for (auto const& poly : multi_poly.polygons)
        {
            bool first = true;
            mapnik::new_geometry::polygon polygon;
            if (poly.size() > 1) polygon.interior_rings.reserve(poly.size() - 1);

            for (auto const& ring : poly)
            {
                mapnik::new_geometry::linear_ring linear_ring;
                for (auto const& index : ring)
                {
                    double px = 0, py = 0;
                    bool reverse = index < 0;
                    index_type arc_index = reverse ? std::abs(index) - 1 : index;
                    auto const& coords = topo_.arcs[arc_index].coordinates;
                    processed_coords.clear();
                    processed_coords.reserve(coords.size());
                    for (auto const& pt : coords )
                    {
                        double x = pt.x;
                        double y = pt.y;

                        if (topo_.tr)
                        {
                            x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
                            y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
                        }
                        processed_coords.emplace_back(coordinate{x,y});
                    }

                    using namespace boost::adaptors;

                    if (reverse)
                    {
                        for (auto const& c : (processed_coords | reversed))
                        {
                            linear_ring.add_coord(c.x, c.y);
                        }
                    }
                    else
                    {
                        for (auto const& c : processed_coords)
                        {
                            linear_ring.add_coord(c.x, c.y);
                        }
                    }
                }
                if (first)
                {
                    first = false;
                    polygon.set_exterior_ring(std::move(linear_ring));
                }
                else
                {
                    polygon.add_hole(std::move(linear_ring));
                }
            }
            multi_polygon.push_back(std::move(polygon));
        }
        feature->set_geometry(std::move(multi_polygon));
        assign_properties(*feature, multi_poly, tr_);
        return feature;
    }

    template<typename T>
    feature_ptr operator() (T const& ) const
    {
        return feature_ptr();
    }

    Context & ctx_;
    mapnik::transcoder const& tr_;
    topology const& topo_;
    std::size_t feature_id_;
};

}}

topojson_featureset::topojson_featureset(mapnik::topojson::topology const& topo,
                                         mapnik::transcoder const& tr,
                                         array_type && index_array)
    : ctx_(std::make_shared<mapnik::context_type>()),
      topo_(topo),
      tr_(tr),
      index_array_(std::move(index_array)),
      index_itr_(index_array_.begin()),
      index_end_(index_array_.end()),
      feature_id_ (0) {}

topojson_featureset::~topojson_featureset() {}

mapnik::feature_ptr topojson_featureset::next()
{
    if (index_itr_ != index_end_)
    {
        topojson_datasource::item_type const& item = *index_itr_++;
        std::size_t index = item.second;
        if ( index < topo_.geometries.size())
        {
            mapnik::topojson::geometry const& geom = topo_.geometries[index];
            mapnik::feature_ptr feature = mapnik::util::apply_visitor(
                mapnik::topojson::feature_generator<mapnik::context_ptr>(ctx_, tr_, topo_, feature_id_++),
                geom);
            return feature;
        }
    }

    return mapnik::feature_ptr();
}
<|endoftext|>


<commit_before>/*
    This file is part of kdepim.

    Copyright (c) 2004 Till Adam <adam@kde.org>

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#include <interfaces/bodypartformatter.h>
#include <interfaces/bodypart.h>
#include <interfaces/bodyparturlhandler.h>
#include <khtmlparthtmlwriter.h>

#include <kmail/callback.h>
#include <kmail/kmmessage.h>

#include <kglobal.h>
#include <klocale.h>
#include <kstringhandler.h>
#include <kglobalsettings.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kapplication.h>
#include <kdemacros.h>

#include <QUrl>
#include <QFile>
#include <QDir>
#include <QTextDocument>

namespace {

  // TODO: Show filename header to make it possible to save the patch.
  // FIXME: The box should only be as wide as necessary.

  class Formatter : public KMail::Interface::BodyPartFormatter {
  public:
    Result format( KMail::Interface::BodyPart *bodyPart, KMail::HtmlWriter *writer ) const {

      if ( !writer ) return Ok;

      if (  bodyPart->defaultDisplay() == KMail::Interface::BodyPart::AsIcon )
        return AsIcon;

      const QString diff = bodyPart->asText();
      if ( diff.isEmpty() ) return AsIcon;

      static const QLatin1String addedLineStyle( "style=\"" "color: green;\"" );
      static const QLatin1String fileAddStyle( "style=\"font-weight: bold; " "color: green; \"" );

      static const QLatin1String removedLineStyle( "style=\"" "color: red;\"" );
      static const QLatin1String fileRemoveStyle( "style=\"font-weight: bold; "
                               "color: red ;\"" );

      static const QLatin1String tableStyle(
        "style=\""
        "text-align: -khtml-auto; "
        "border: solid black 1px; "
        "padding: 0.5em; "
        "margin: 0em;\"");

      static const QLatin1String sepStyle( "style=\"color: black; font-weight: bold;\"" );
      static const QLatin1String chunkStyle( "style=\"color: blue;\"" );

      QString html = "

";
      html += "
';

      const QStringList lines = diff.split( '\n' );
      for ( QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it ) {
        const QString line( Qt::escape( *it ) );
        QString style;
        if ( !line.isEmpty() ) {
          if ( line.startsWith( "+++" ) ) {
            style = fileAddStyle;
          } else if ( line.startsWith( QLatin1String("---") ) ) {
            style = fileRemoveStyle;
          } else if ( line.startsWith( '+' ) || line.startsWith( '>' ) ) {
            style = addedLineStyle;
          } else if ( line.startsWith( '-' ) || line.startsWith( '<' ) ) {
            style = removedLineStyle;
          } else if ( line.startsWith( QLatin1String("==") ) ) {
            style = sepStyle;
          } else if ( line.startsWith( QLatin2String("@@") ) ) {
            style = chunkStyle;
          }
        }
        html += "' + line + "
";
      }

      html += "

";
      //kDebug( "%s", html.toLatin1() );
      writer->queue( html );

      return Ok;
    }
  };

  class Plugin : public KMail::Interface::BodyPartFormatterPlugin {
  public:
    const KMail::Interface::BodyPartFormatter * bodyPartFormatter( int idx ) const {
      return idx == 0 ? new Formatter() : 0 ;
    }
    const char * type( int idx ) const {
      return idx == 0 ? "text" : 0 ;
    }
    const char * subtype( int idx ) const {
      return idx == 0 ? "x-diff" : 0 ;
    }

    const KMail::Interface::BodyPartURLHandler * urlHandler( int ) const { return 0; }
  };

}

extern "C"
KDE_EXPORT KMail::Interface::BodyPartFormatterPlugin *
kmail_bodypartformatter_text_xdiff_create_bodypart_formatter_plugin() {
  return new Plugin();
}
<commit_msg>Build.<commit_after>/*
    This file is part of kdepim.

    Copyright (c) 2004 Till Adam <adam@kde.org>

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#include <interfaces/bodypartformatter.h>
#include <interfaces/bodypart.h>
#include <interfaces/bodyparturlhandler.h>
#include <khtmlparthtmlwriter.h>

#include <kmail/callback.h>
#include <kmail/kmmessage.h>

#include <kglobal.h>
#include <klocale.h>
#include <kstringhandler.h>
#include <kglobalsettings.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kapplication.h>
#include <kdemacros.h>

#include <QUrl>
#include <QFile>
#include <QDir>
#include <QTextDocument>

namespace {

  // TODO: Show filename header to make it possible to save the patch.
  // FIXME: The box should only be as wide as necessary.

  class Formatter : public KMail::Interface::BodyPartFormatter {
  public:
    Result format( KMail::Interface::BodyPart *bodyPart, KMail::HtmlWriter *writer ) const {

      if ( !writer ) return Ok;

      if (  bodyPart->defaultDisplay() == KMail::Interface::BodyPart::AsIcon )
        return AsIcon;

      const QString diff = bodyPart->asText();
      if ( diff.isEmpty() ) return AsIcon;

      static const QLatin1String addedLineStyle( "style=\"" "color: green;\"" );
      static const QLatin1String fileAddStyle( "style=\"font-weight: bold; " "color: green; \"" );

      static const QLatin1String removedLineStyle( "style=\"" "color: red;\"" );
      static const QLatin1String fileRemoveStyle( "style=\"font-weight: bold; "
                               "color: red ;\"" );

      static const QLatin1String tableStyle(
        "style=\""
        "text-align: -khtml-auto; "
        "border: solid black 1px; "
        "padding: 0.5em; "
        "margin: 0em;\"");

      static const QLatin1String sepStyle( "style=\"color: black; font-weight: bold;\"" );
      static const QLatin1String chunkStyle( "style=\"color: blue;\"" );

      QString html = "

";
      html += "
';

      const QStringList lines = diff.split( '\n' );
      for ( QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it ) {
        const QString line( Qt::escape( *it ) );
        QString style;
        if ( !line.isEmpty() ) {
          if ( line.startsWith( "+++" ) ) {
            style = fileAddStyle;
          } else if ( line.startsWith( QLatin1String("---") ) ) {
            style = fileRemoveStyle;
          } else if ( line.startsWith( '+' ) || line.startsWith( '>' ) ) {
            style = addedLineStyle;
          } else if ( line.startsWith( '-' ) || line.startsWith( '<' ) ) {
            style = removedLineStyle;
          } else if ( line.startsWith( QLatin1String("==") ) ) {
            style = sepStyle;
          } else if ( line.startsWith( QLatin1String("@@") ) ) {
            style = chunkStyle;
          }
        }
        html += "' + line + "
";
      }

      html += "

";
      //kDebug( "%s", html.toLatin1() );
      writer->queue( html );

      return Ok;
    }
  };

  class Plugin : public KMail::Interface::BodyPartFormatterPlugin {
  public:
    const KMail::Interface::BodyPartFormatter * bodyPartFormatter( int idx ) const {
      return idx == 0 ? new Formatter() : 0 ;
    }
    const char * type( int idx ) const {
      return idx == 0 ? "text" : 0 ;
    }
    const char * subtype( int idx ) const {
      return idx == 0 ? "x-diff" : 0 ;
    }

    const KMail::Interface::BodyPartURLHandler * urlHandler( int ) const { return 0; }
  };

}

extern "C"
KDE_EXPORT KMail::Interface::BodyPartFormatterPlugin *
kmail_bodypartformatter_text_xdiff_create_bodypart_formatter_plugin() {
  return new Plugin();
}
<|endoftext|>