Click here to show toolbars of the Web Online Help System: show toolbars
 

//***************************************************************************************
// 
// File name: 3dPlanarity.cpp  
// Location: See Matrox Example Launcher in the MIL Control Center
// 
//
// Synopsis: Demonstrates object planarity measurements of a
//           scanned 3d mechanical part.
//
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2016.
// All Rights Reserved

#include <mil.h>
#include <math.h>
#include <vector>
using std::vector;

// Preprocessor switch to enable a 3D display.
#if M_MIL_USE_WINDOWS && !M_MIL_USE_CE && !M_MIL_USE_RT 
#define ENABLE_DISPLAY_3D  1
#else
#define ENABLE_DISPLAY_3D  0
#endif

//****************************************************************************
// Example description.
//****************************************************************************
void PrintHeader()   
   {
   MosPrintf(MIL_TEXT("[EXAMPLE NAME]\n"));
   MosPrintf(MIL_TEXT("3dPlanarity\n\n"));

   MosPrintf(MIL_TEXT("[SYNOPSIS]\n"));
   MosPrintf(MIL_TEXT("This example demonstrates how to perform planarity measurements\n")
             MIL_TEXT("on a 3d reconstruction of a mechanical part."));
   MosPrintf(MIL_TEXT("\n\n"));

   MosPrintf(MIL_TEXT("[MODULES USED]\n"));
   MosPrintf(MIL_TEXT("Modules used: 3d reconstruction, application, buffer, calibration,\n")
             MIL_TEXT("display, graphic, image processing, model finder, system.\n\n"));
   }

//****************************************************************************
// Constants.
//****************************************************************************
static const MIL_DOUBLE DIV_180_PI = 57.295779513082320866997945294156;

static const MIL_INT MAP_SIZE_X = 515;                // in pixels
static const MIL_INT MAP_SIZE_Y = 880;                // in pixels

static const MIL_DOUBLE PLANE_FIT_CENTER_X = 47.88;   // in mm
static const MIL_DOUBLE PLANE_FIT_CENTER_Y = 39.29;   // in mm
static const MIL_DOUBLE PLANE_FIT_RADIUS = 23;        // in mm

// Offsets of the windows.
static const MIL_INT WINDOWS_OFFSET_X = 15;
static const MIL_INT WINDOWS_OFFSET_Y = 38;

//****************************************************************************
// 3d display.
//****************************************************************************
#if ENABLE_DISPLAY_3D
#include <MdispD3D.h>

// 3D display parameters.
static const MIL_INT    DISPLAY_3D_SIZE_X    = 800;               // in pixels
static const MIL_INT    DISPLAY_3D_SIZE_Y    = 600;               // in pixels
static const MIL_DOUBLE DISPLAY_3D_VOLUME    = MD3D_TRANSPARENT;
static const MIL_DOUBLE DISPLAY_3D_LOOK_AT_X = 66.53;             // in mm
static const MIL_DOUBLE DISPLAY_3D_LOOK_AT_Y = 268.84;            // in mm
static const MIL_DOUBLE DISPLAY_3D_LOOK_AT_Z = 38.96;             // in mm
static const MIL_DOUBLE DISPLAY_3D_EYE_DIST  = 60.40;             // in mm
static const MIL_DOUBLE DISPLAY_3D_EYE_THETA = -107.68;           // in deg
static const MIL_DOUBLE DISPLAY_3D_EYE_PHI   = 63.20;             // in deg

// Window offset parameter.
static const MIL_INT ILLUSTRATION_DISPLAY_OFFSET_X = DISPLAY_3D_SIZE_X;
#else
static const MIL_INT ILLUSTRATION_DISPLAY_OFFSET_X = MAP_SIZE_X;
#endif

//****************************************************************************
// File names.
//****************************************************************************
#define DATA_EX_PATH(x) (M_IMAGE_PATH MIL_TEXT("MechanicalPartScan/") MIL_TEXT(x))
static MIL_CONST_TEXT_PTR METAL_PART_CLOUD_CONTAINER = DATA_EX_PATH("MetalPartCloudContainer.m3d");
#define EX_PATH(x) (M_IMAGE_PATH MIL_TEXT("3dPlanarity/") MIL_TEXT(x))
static MIL_CONST_TEXT_PTR MEASURES_ILLUSTRATIONS[5] =
   {
   EX_PATH("DepthVerticalIllustration.mim"),
   EX_PATH("DepthPlaneVerticalIllustration.mim"),
   EX_PATH("DepthPlaneNormalIllustration.mim"),
   EX_PATH("PointPlaneWorldCylinder.mim"),
   EX_PATH("PointPlaneRaster.mim")
   };

//****************************************************************************
// Useful structures.
//****************************************************************************
struct SMapGeneration
   {
   MIL_DOUBLE BoxCornerX;
   MIL_DOUBLE BoxCornerY;
   MIL_DOUBLE BoxCornerZ;
   MIL_DOUBLE BoxSizeX;
   MIL_DOUBLE BoxSizeY;
   MIL_DOUBLE BoxSizeZ;
   MIL_INT    MapSizeX;
   MIL_INT    MapSizeY;
   MIL_INT    IntensityMapType;
   bool       SetExtractOverlap;
   MIL_DOUBLE ExtractOverlap;
   MIL_DOUBLE FillXThreshold;
   MIL_DOUBLE FillYThreshold;
   };

struct SPlanarityMeasure
   {
   SPlanarityMeasure(MIL_CONST_TEXT_PTR Name) : MeasureName(Name){};
   MIL_CONST_TEXT_PTR MeasureName;
   MIL_DOUBLE Planarity;
   };

//****************************************************************************
// Function prototypes.
//****************************************************************************
void AnalyzePlanarity(MIL_ID MilSystem, MIL_ID PointCloudContainer);

void GenerateDepthMap(MIL_ID MilSystem, 
                      MIL_ID PointCloudContainer, 
                      const SMapGeneration& GenerationInfo, 
                      MIL_ID& OutDepthmap);

bool FixturePart(MIL_ID MilSystem, 
                 MIL_ID MilDepthMap, 
                 MIL_ID MilFixtureDestination, 
                 MIL_ID MilGraList);

void PrintResultTable(const vector<SPlanarityMeasure>& rPlanarityMeasures);

bool CheckForRequiredMILFile(MIL_CONST_TEXT_PTR FileName);

//*****************************************************************************
// Main.
//*****************************************************************************
int MosMain(void)
   {
   PrintHeader();

   // Allocate the MIL application.
   MIL_ID MilApplication = MappAlloc(M_NULL, M_DEFAULT, M_NULL);

   // Check for required example files.
   if (!CheckForRequiredMILFile(METAL_PART_CLOUD_CONTAINER))
      {
      MappFree(MilApplication);
      return -1;
      }

   // Allocate a host system.
   MIL_ID MilSystem = MsysAlloc(M_DEFAULT, M_SYSTEM_HOST, M_DEFAULT, M_DEFAULT, M_NULL);

   MosPrintf(MIL_TEXT("Press <Enter> to start.\n\n"));
   MosGetch();

   // Restore a 3d point cloud of the object.
   MIL_ID PointCloudContainer = M_NULL;
   M3dmapRestore(METAL_PART_CLOUD_CONTAINER, MilSystem, M_DEFAULT, &PointCloudContainer);
   
   // Analyze.
   AnalyzePlanarity(MilSystem, PointCloudContainer);

   // Free the MIL 3d point cloud.
   M3dmapFree(PointCloudContainer);

   // Free the MIL system and application.
   MsysFree(MilSystem);
   MappFree(MilApplication);

   return 0;
   }

//*******************************************************************************
// AnalyzePlanarity. Planarity analysis of the the scanned object.
//*******************************************************************************
void AnalyzePlanarity(MIL_ID MilSystem, MIL_ID MilPointCloudContainer)
   {
   // Allocates the displays and graphic lists
   MIL_ID MilIllustrationDisplay = MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"),
                                              M_WINDOWED, M_NULL);
   MdispControl(MilIllustrationDisplay, M_WINDOW_INITIAL_POSITION_X,
                ILLUSTRATION_DISPLAY_OFFSET_X + WINDOWS_OFFSET_X);
   MIL_ID MilDisplayDepthMap = MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"),
                                          M_WINDOWED, M_NULL);
   MIL_ID MilDepthMapGraphicList  = MgraAllocList(MilSystem, M_DEFAULT, M_NULL);
   MdispControl(MilDisplayDepthMap, M_ASSOCIATED_GRAPHIC_LIST_ID, MilDepthMapGraphicList);
   MdispControl(MilDisplayDepthMap, M_VIEW_MODE, M_AUTO_SCALE);
   
   // Set the volume information.
   SMapGeneration MapData;
   MapData.BoxCornerX = 5.00;
   MapData.BoxCornerY = 0;
   MapData.BoxCornerZ = -4.00;
   MapData.BoxSizeX   = 120.00;
   MapData.BoxSizeY   = 250.00;
   MapData.BoxSizeZ   = -30.00;
   MapData.MapSizeX   = MAP_SIZE_X;
   MapData.MapSizeY   = MAP_SIZE_Y;
   MapData.SetExtractOverlap = true;
   MapData.ExtractOverlap = M_MIN;
   MapData.FillXThreshold = 1.0;
   MapData.FillYThreshold = 1.0;

   // Generate the top view calibrated depth map of the mechanical part.
   MIL_ID MilDepthMap = M_NULL;
   GenerateDepthMap(MilSystem, MilPointCloudContainer, MapData, MilDepthMap);

   // Allocate the necessary buffers for processing and display.
   MIL_ID MilPlaneFitMask = MbufAlloc2d(MilSystem,
                                        MAP_SIZE_X,
                                        MAP_SIZE_Y,
                                        8, M_IMAGE + M_PROC, M_NULL);

   // Allocate an image that will hold the calibration for the coordinates transformations.
   MIL_ID MilCalHolder = MbufAlloc2d(MilSystem, 2, 2, 16 + M_UNSIGNED, M_IMAGE + M_PROC, M_NULL);

   // Allocate the plane geometry.
   MIL_ID MilPlaneGeometry = M3dmapAlloc(MilSystem, M_GEOMETRY, M_DEFAULT, M_NULL);

   // Display the depth map and its calibration.
   MgraClear(M_DEFAULT, MilDepthMapGraphicList);
   MgraColor(M_DEFAULT, M_COLOR_LIGHT_BLUE);
   MgraBackColor(M_DEFAULT, M_COLOR_GRAY);
   McalDraw(M_DEFAULT, MilDepthMap, MilDepthMapGraphicList,
            M_DRAW_RELATIVE_COORDINATE_SYSTEM, M_DEFAULT, M_DEFAULT);
   MdispSelect(MilDisplayDepthMap, MilDepthMap);

   MosPrintf(MIL_TEXT("A top view calibrated depth map of the mechanical part was generated.\n\n")
             MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

   // Locate the part and move the relative coordinate system accordingly.
   bool PartFound = FixturePart(MilSystem, MilDepthMap, MilDepthMap, MilDepthMapGraphicList);

   if (PartFound)
      {
      // Move the relative to the plane location.
      McalFixture(MilDepthMap, M_NULL, M_MOVE_RELATIVE, M_POINT_AND_ANGLE, M_DEFAULT,
                  PLANE_FIT_CENTER_X, PLANE_FIT_CENTER_Y, 0.0, M_DEFAULT);
      MgraColor(M_DEFAULT, M_COLOR_DARK_BLUE);
      McalDraw(M_DEFAULT, MilDepthMap, MilDepthMapGraphicList,
               M_DRAW_RELATIVE_COORDINATE_SYSTEM, M_DEFAULT, M_DEFAULT);

      MosPrintf(MIL_TEXT("The part was located and fixtured using Model Finder in the depth map.\n\n")
                MIL_TEXT("Press <Enter> to continue.\n\n"));
      MosGetch();
      
      // Disable the display updates.
      MdispControl(MilDisplayDepthMap, M_UPDATE, M_DISABLE);

      // Create a mask where we want to measure the planarity.
      MbufClear(MilPlaneFitMask, 0);
      McalAssociate(MilDepthMap, MilPlaneFitMask, M_DEFAULT);
      MgraControl(M_DEFAULT, M_INPUT_UNITS, M_WORLD);
      MgraColor(M_DEFAULT, 255);
      MgraArcFill(M_DEFAULT, MilPlaneFitMask, 0.0, 0.0,
                  PLANE_FIT_RADIUS, PLANE_FIT_RADIUS, 0, 360);
      MgraColor(M_DEFAULT, M_COLOR_MAGENTA);
      MgraArcFill(M_DEFAULT, MilDepthMapGraphicList, 0.0, 0.0,
                  PLANE_FIT_RADIUS, PLANE_FIT_RADIUS, 0, 360);
      MgraControl(M_DEFAULT, M_INPUT_UNITS, M_PIXEL);

      // Show the region where we want to measure the planarity.
      MosPrintf(MIL_TEXT("The planarity of the depth data in the magenta region will be evaluated.\n\n")
                MIL_TEXT("Press <Enter> to continue.\n\n"));
      MdispControl(MilDisplayDepthMap, M_UPDATE, M_ENABLE);
      MosGetch();

      // Disable the updates and remove the fill of the graphic that represents the region.
      MdispControl(MilDisplayDepthMap, M_UPDATE, M_DISABLE);
      MgraControlList(MilDepthMapGraphicList, M_GRAPHIC_INDEX(3), M_DEFAULT, M_FILLED, M_FALSE);
      
      // Allocate an empty point cloud.
      MIL_DOUBLE InvalidVal = M_INVALID_POINT;
      M3dmapPut(MilPointCloudContainer, M_POINT_CLOUD_LABEL(3), M_POSITION, 64 + M_FLOAT,
                1, &InvalidVal, &InvalidVal, &InvalidVal, M_NULL, M_DEFAULT);
      
      // Measure the vertical planarity. Get the depth map minimum and maximum Z value
      // in the mask.
      MIL_DOUBLE MinZ;
      MIL_DOUBLE MaxZ;
      SPlanarityMeasure VerticalDepthMeasure(MIL_TEXT("Depth map with horizonal plane"));
      M3dmapSetGeometry(MilPlaneGeometry, M_HORIZONTAL_PLANE, M_PARAMETRIC,
                        MapData.BoxCornerZ, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT);
      M3dmapStat(MilDepthMap, MilPlaneGeometry, MilPlaneFitMask, M_NULL,
                 M_DEVIATION_MAX, M_INFINITE, M_DEFAULT, &VerticalDepthMeasure.Planarity);
      MinZ = MapData.BoxCornerZ - VerticalDepthMeasure.Planarity;
      M3dmapSetGeometry(MilPlaneGeometry, M_HORIZONTAL_PLANE, M_PARAMETRIC,
                        MinZ, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT);
      M3dmapStat(MilDepthMap, MilPlaneGeometry, MilPlaneFitMask, M_NULL,
                 M_DEVIATION_MAX, M_INFINITE, M_DEFAULT, &VerticalDepthMeasure.Planarity);

      // Re-extract the depth map for display purposes. The resulting depth map only contains data 
      // in the vertical planarity region.
      MapData.BoxCornerZ = MinZ;
      MapData.BoxSizeZ = VerticalDepthMeasure.Planarity;
      GenerateDepthMap(MilSystem, MilPointCloudContainer, MapData, MilDepthMap);

      // Put back the fixture found previously.
      McalFixture(MilDepthMap, M_NULL, M_MOVE_RELATIVE, M_SAME_AS_SOURCE,
                  MilPlaneFitMask, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT);

      // Clear the depth map data. Put all depth map values outside the mask to invalid data.
      MbufClearCond(MilDepthMap, MIL_UINT16_MAX, 0, 0, MilPlaneFitMask, M_EQUAL, 0.0);

      // Print message.
      MosPrintf(MIL_TEXT("The depth data used to measure the planarity is displayed.\n\n")
                MIL_TEXT("Press <Enter> to continue.\n\n"));
      
      MdispControl(MilDisplayDepthMap, M_UPDATE, M_ENABLE);
      MosGetch();
      
      // Move the relative coordinate system of the point cloud at the fixtured position.
      // and extract an empty point cloud to propagate the calibration.
      McalFixture(MilPointCloudContainer, M_NULL, M_MOVE_RELATIVE, M_SAME_AS_SOURCE,
                  MilDepthMap, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT);
      M3dmapExtract(MilPointCloudContainer, MilCalHolder, M_NULL,
                    M_CORRECTED_DEPTH_MAP, M_POINT_CLOUD_LABEL(3), M_DEFAULT);

#if ENABLE_DISPLAY_3D
      // Display a 3d display where a depth map represents the planarity measure.
      MIL_DISP_D3D_HANDLE MilDispD3D = MdepthSysD3DAlloc(M_NULL, M_NULL, M_NULL, M_NULL,
         &MilDepthMap, M_NULL, 1, DISPLAY_3D_SIZE_X, DISPLAY_3D_SIZE_Y, M_DEFAULT, M_NULL);
      
      // Set rendering parameters.
      MdispD3DControl(MilDispD3D, MD3D_VOLUME, DISPLAY_3D_VOLUME);

      // Set camera pose.
      MdispD3DControl(MilDispD3D, MD3D_LOOK_AT_X, DISPLAY_3D_LOOK_AT_X);
      MdispD3DControl(MilDispD3D, MD3D_LOOK_AT_Y, DISPLAY_3D_LOOK_AT_Y);
      MdispD3DControl(MilDispD3D, MD3D_LOOK_AT_Z, DISPLAY_3D_LOOK_AT_Z);
      MdispD3DControl(MilDispD3D, MD3D_EYE_THETA, DISPLAY_3D_EYE_THETA);
      MdispD3DControl(MilDispD3D, MD3D_EYE_PHI, DISPLAY_3D_EYE_PHI);
      MdispD3DControl(MilDispD3D, MD3D_EYE_DIST, DISPLAY_3D_EYE_DIST);

      MdispD3DShow(MilDispD3D);
#endif
      // Show the vertical planarity measure.
      MIL_ID MilIllustrationImage = MbufRestore(MEASURES_ILLUSTRATIONS[0], MilSystem, M_NULL);
      MdispSelect(MilIllustrationDisplay, MilIllustrationImage);
      MosPrintf(MIL_TEXT("First, the planarity of the surface along the Z axis is measured.\n")
                MIL_TEXT("Since the analyzed plane is not coplanar to the XY plane, the planarity\n")
                MIL_TEXT("measure mostly reflects the plane's tilt.\n\n"));
      
      vector<SPlanarityMeasure> AllMeasures(1, VerticalDepthMeasure);
      PrintResultTable(AllMeasures);
      MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
      MosGetch();

      // Fit a plane on the data.
      M3dmapSetGeometry(MilPlaneGeometry, M_PLANE, M_FIT, (MIL_DOUBLE)MilDepthMap,
                        (MIL_DOUBLE)MilPlaneFitMask, M_DEFAULT, M_DEFAULT, M_DEFAULT);
      
      // Get the parameters of the plane.
      MIL_DOUBLE Ax;
      MIL_DOUBLE Ay;
      MIL_DOUBLE Z0;
      M3dmapInquire(MilPlaneGeometry, M_DEFAULT, M_FIT_PARAM_AX, &Ax);
      M3dmapInquire(MilPlaneGeometry, M_DEFAULT, M_FIT_PARAM_AY, &Ay);
      M3dmapInquire(MilPlaneGeometry, M_DEFAULT, M_FIT_PARAM_Z0, &Z0);

      // Measure the planarity with regards to the fitted plane.
      MIL_DOUBLE NegativeMaxDeviation;
      MIL_DOUBLE PositiveMaxDeviation;
      M3dmapStat(MilDepthMap, MilPlaneGeometry, MilPlaneFitMask, M_NULL,
                 M_DEVIATION_MAX + M_STAT_NEGATIVE, M_INFINITE, M_DEFAULT, &NegativeMaxDeviation);
      M3dmapStat(MilDepthMap, MilPlaneGeometry, MilPlaneFitMask, M_NULL,
                 M_DEVIATION_MAX + M_STAT_POSITIVE, M_INFINITE, M_DEFAULT, &PositiveMaxDeviation);
      
      // Use the plane parameters to move the output coordinate system of the point cloud container.
      MIL_DOUBLE Norm = sqrt((Ax * Ax) + (Ay * Ay) + 1);
      MIL_DOUBLE AxisX = -Ay;
      MIL_DOUBLE AxisY = Ax;
      MIL_DOUBLE AxisZ = 0;
      MIL_DOUBLE CrossNorm = sqrt((AxisX * AxisX) + (AxisY * AxisY) + (AxisZ * AxisZ));
      MIL_DOUBLE RotationAngleRad = asin(CrossNorm / Norm);
      MIL_DOUBLE RotationAngle = DIV_180_PI * RotationAngleRad;
      
      McalSetCoordinateSystem(MilPointCloudContainer,
                              M_RELATIVE_COORDINATE_SYSTEM,
                              M_RELATIVE_COORDINATE_SYSTEM,
                              M_TRANSLATION + M_COMPOSE_WITH_CURRENT,
                              M_NULL,
                              0.0,
                              0.0,
                              Z0,
                              M_DEFAULT);

      McalSetCoordinateSystem(MilPointCloudContainer,
                              M_RELATIVE_COORDINATE_SYSTEM,
                              M_RELATIVE_COORDINATE_SYSTEM,
                              M_ROTATION_AXIS_ANGLE + M_COMPOSE_WITH_CURRENT,
                              M_NULL,
                              AxisX / CrossNorm,
                              AxisY / CrossNorm,
                              AxisZ / CrossNorm,
                              -RotationAngle);

      SPlanarityMeasure PlaneDepthVerticalMeasure(MIL_TEXT("Depth map with fitted plane (vertical)"));
      PlaneDepthVerticalMeasure.Planarity = NegativeMaxDeviation + PositiveMaxDeviation;;
      NegativeMaxDeviation *= cos(RotationAngleRad);
      PositiveMaxDeviation *= cos(RotationAngleRad);
      SPlanarityMeasure PlaneDepthNormalMeasure(MIL_TEXT("Depth map with fitted plane (normal)"));
      PlaneDepthNormalMeasure.Planarity = NegativeMaxDeviation + PositiveMaxDeviation;

      // Show the depth map plane vertical planarity measures.
      MosPrintf(MIL_TEXT("By fitting a plane on the depth map data, the planarity of the surface\n")
                MIL_TEXT("along the Z axis can be measured.\n\n"));

      MbufLoad(MEASURES_ILLUSTRATIONS[1], MilIllustrationImage);
      AllMeasures.push_back(PlaneDepthVerticalMeasure);
      PrintResultTable(AllMeasures);
      MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
      MosGetch();

      // Show the depth map plane normal planarity measure.
      MosPrintf(MIL_TEXT("By calculating the tilt from the plane equation, the planarity along\n")
                MIL_TEXT("the fitted plane normal can be deduced.\n\n"));

      MbufLoad(MEASURES_ILLUSTRATIONS[2], MilIllustrationImage);
      AllMeasures.push_back(PlaneDepthNormalMeasure);
      PrintResultTable(AllMeasures);
      MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
      MosGetch();

      // Get the 3d points in the relative coordinate system.
      MIL_INT NbPoints;
      M3dmapGet(MilPointCloudContainer, M_POINT_CLOUD_INDEX(0), M_POSITION, M_EXCLUDE_INVALID_POINTS,
                64 + M_FLOAT, M_NULL, M_NULL, M_NULL, M_NULL, &NbPoints);
      vector<MIL_DOUBLE> XRel(NbPoints);
      vector<MIL_DOUBLE> YRel(NbPoints);
      vector<MIL_DOUBLE> ZRel(NbPoints);
      M3dmapGet(MilPointCloudContainer, M_POINT_CLOUD_INDEX(0), M_POSITION, M_EXCLUDE_INVALID_POINTS,
                64 + M_FLOAT, NbPoints, &XRel[0], &YRel[0], &ZRel[0], &NbPoints);

      // Get the minimum and maximum Z value of the points in the arc.
      MIL_DOUBLE PlaneRadiusSqr = PLANE_FIT_RADIUS * PLANE_FIT_RADIUS;
      MinZ = MIL_DOUBLE_MAX;
      MaxZ = -MIL_DOUBLE_MAX;
      for (MIL_INT p = 0; p < NbPoints; p++)
         {
         MIL_DOUBLE DistXYSqr = XRel[p] * XRel[p] + YRel[p] * YRel[p];
         if (DistXYSqr < PlaneRadiusSqr)
            {
            if (ZRel[p] < MinZ)
               MinZ = ZRel[p];
            else if (ZRel[p] > MaxZ)
               MaxZ = ZRel[p];
            }
         }
      SPlanarityMeasure PlanePlanarityDiskMaskMeasure(MIL_TEXT("Point cloud with fitted plane ")
                                                      MIL_TEXT("(world relative disk region)"));
      PlanePlanarityDiskMaskMeasure.Planarity = MaxZ - MinZ;
      
      // Show the point cloud with virtual cylindrical region measure.
      MosPrintf(MIL_TEXT("By fixturing the relative coordinate system on the fitted plane, it is\n")
                MIL_TEXT("possible to get and select the points of the point cloud in a virtual\n")
                MIL_TEXT("cylindrical region whose axis is aligned with the plane normal.\n")
                MIL_TEXT("The difference between the minimum and maximum fixtured Z value \n")
                MIL_TEXT("provides the planarity of the points of the surface's points.\n\n"));

      MbufLoad(MEASURES_ILLUSTRATIONS[3], MilIllustrationImage);
      AllMeasures.push_back(PlanePlanarityDiskMaskMeasure);
      PrintResultTable(AllMeasures);
      MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
      MosGetch();
      
      // Convert the points to the absolute.
      vector<MIL_DOUBLE> XAbs(NbPoints);
      vector<MIL_DOUBLE> YAbs(NbPoints);
      vector<MIL_DOUBLE> ZAbs(NbPoints);
      McalTransformCoordinate3dList(MilCalHolder, M_RELATIVE_COORDINATE_SYSTEM, M_ABSOLUTE_COORDINATE_SYSTEM,
                                    NbPoints, &XRel[0], &YRel[0], &ZRel[0], &XAbs[0], &YAbs[0], &ZAbs[0], M_DEFAULT);

      // Convert to pixel coordinates.
      vector<MIL_DOUBLE> XPix(NbPoints);
      vector<MIL_DOUBLE> YPix(NbPoints);
      McalTransformCoordinate3dList(MilPlaneFitMask, M_ABSOLUTE_COORDINATE_SYSTEM, M_PIXEL_COORDINATE_SYSTEM,
                                    NbPoints, &XAbs[0], &YAbs[0], &ZAbs[0], &XPix[0], &YPix[0], M_NULL, M_DEPTH_MAP);

      // Get the value of the list of pixels.
      vector<MIL_UINT8> IsInMask(NbPoints);
      MbufGetList(MilPlaneFitMask, NbPoints, &XPix[0], &YPix[0], M_NEAREST_NEIGHBOR, &IsInMask[0]);

      // Compute the point cloud relative MinZ and MaxZ of points that fall in the mask.
      MinZ = MIL_DOUBLE_MAX;
      MaxZ = -MIL_DOUBLE_MAX;
      for (MIL_INT p = 0; p < NbPoints; p++)
         {
         if (IsInMask[p] == 255)
            {
            if (ZRel[p] < MinZ)
               MinZ = ZRel[p];
            else if (ZRel[p] > MaxZ)
               MaxZ = ZRel[p];
            }
         }
      SPlanarityMeasure PlanePlanarityRasterMaskMeasure(MIL_TEXT("Point cloud with fitted plane ")
                                                        MIL_TEXT("(world initial raster region)"));
      PlanePlanarityRasterMaskMeasure.Planarity = MaxZ - MinZ;

      // Show the point cloud with raster region measure.
      MosPrintf(MIL_TEXT("By transforming the points of the point cloud to the raster mask image,\n")
                MIL_TEXT("it is also possible to select the points based on whether they fall\n")
                MIL_TEXT("in the raster portion of the mask image.\n\n"));
      
      MbufLoad(MEASURES_ILLUSTRATIONS[4], MilIllustrationImage);
      AllMeasures.push_back(PlanePlanarityRasterMaskMeasure);
      PrintResultTable(AllMeasures);
      MosPrintf(MIL_TEXT("Press <Enter> to end.\n\n"));
      MosGetch();

      MbufFree(MilIllustrationImage);

#if ENABLE_DISPLAY_3D
      MdispD3DFree(MilDispD3D);
#endif
      }
   else
      {
      MosPrintf(MIL_TEXT("Unable to find the part in the corrected depth map.\n")
         MIL_TEXT("Press <Enter> to end.\n\n"));
      MosGetch();
      }

   // Free the allocated resources.
   M3dmapFree(MilPlaneGeometry);
   MbufFree(MilCalHolder);
   MbufFree(MilPlaneFitMask);
   MbufFree(MilDepthMap);
   MdispFree(MilDisplayDepthMap);
   MdispFree(MilIllustrationDisplay);
   MgraFree(MilDepthMapGraphicList);
   }

//****************************************************************************
// FixturePart. Find the model, fixture a destination and draw the occurrence
//              in the graphic list.
//****************************************************************************
bool FixturePart(MIL_ID MilSystem, 
                 MIL_ID MilDepthMap, 
                 MIL_ID MilFixtureDestination, 
                 MIL_ID MilGraList)
   {
   // Restore and setup the model used to fixture the part.
   const MIL_TEXT_CHAR* MECHANICAL_PART_MODEL = DATA_EX_PATH("ModelFinderContext.mmf");

   MIL_ID ModelCtx = MmodRestore(MECHANICAL_PART_MODEL, MilSystem, M_WITH_CALIBRATION, M_NULL);
   MIL_ID ModelRes = MmodAllocResult(MilSystem, M_DEFAULT, M_NULL);
   
   // Preprocess the model finder context.
   MmodPreprocess(ModelCtx, M_DEFAULT);

   // Create the fixturing offset.
   MIL_ID FixtureOffset = McalAlloc(MilSystem, M_FIXTURING_OFFSET, M_DEFAULT, M_NULL);
   McalFixture(M_NULL, FixtureOffset, M_LEARN_OFFSET, M_MODEL_MOD, ModelCtx, 0, M_DEFAULT,
               M_DEFAULT, M_DEFAULT);

   // Allocate the target image.
   MIL_INT SizeX, SizeY;
   MbufInquire(MilDepthMap, M_SIZE_X, &SizeX);
   MbufInquire(MilDepthMap, M_SIZE_Y, &SizeY);
   MIL_ID MilSearchImage = MbufAlloc2d(MilSystem, SizeX, SizeY, 8, M_IMAGE + M_PROC, M_NULL);

   // Remap the depth map to the 8 bit target image.
   MimShift(MilDepthMap, MilSearchImage, -8);

   // Find the model.
   MmodFind(ModelCtx, MilSearchImage, ModelRes);

   // Retrieve the information.
   MIL_INT NumOfOccurences = 0;
   MmodGetResult(ModelRes, M_DEFAULT, M_NUMBER + M_TYPE_MIL_INT, &NumOfOccurences);

   if (NumOfOccurences)
      {
      // Fixture the depth map.
      McalFixture(MilFixtureDestination, FixtureOffset, M_MOVE_RELATIVE, M_RESULT_MOD,
                  ModelRes, 0, M_DEFAULT, M_DEFAULT, M_DEFAULT);

      // Draw the found occurrence.
      MgraColor(M_DEFAULT, M_COLOR_RED);
      MmodDraw(M_DEFAULT, ModelRes, MilGraList, M_DRAW_EDGES + M_MODEL, M_DEFAULT, M_DEFAULT);
      }

   // Release the allocated resources.
   MmodFree(ModelCtx);
   MmodFree(ModelRes);
   MbufFree(MilSearchImage);
   McalFree(FixtureOffset);

   return (NumOfOccurences > 0);
   }

//****************************************************************************
// GenerateDepthMap. Depth map extraction from the point cloud container.
//****************************************************************************
void GenerateDepthMap(MIL_ID MilSystem, 
                      MIL_ID PointCloudContainer, 
                      const SMapGeneration& GenerationInfo, 
                      MIL_ID& OutDepthmap)
   {
   // Allocate the depth map if required.
   if (OutDepthmap == M_NULL)
      {
      MbufAlloc2d(MilSystem,
                  GenerationInfo.MapSizeX,
                  GenerationInfo.MapSizeY,
                  16 + M_UNSIGNED,
                  M_IMAGE + M_PROC + M_DISP,
                  &OutDepthmap);
      }

   // Set the extraction box.
   M3dmapSetBox(PointCloudContainer,
                M_EXTRACTION_BOX,
                M_CORNER_AND_DIMENSION,
                GenerationInfo.BoxCornerX,
                GenerationInfo.BoxCornerY,
                GenerationInfo.BoxCornerZ,
                GenerationInfo.BoxSizeX,
                GenerationInfo.BoxSizeY,
                GenerationInfo.BoxSizeZ);

   // Set extraction properties.
   M3dmapControl(PointCloudContainer, M_DEFAULT, M_EXTRACTION_CUMULATIVE, M_DISABLE);
   M3dmapControl(PointCloudContainer, M_DEFAULT, M_FILL_MODE, M_X_THEN_Y);
   M3dmapControl(PointCloudContainer, M_DEFAULT, M_FILL_SHARP_ELEVATION, M_DISABLE);
   M3dmapControl(PointCloudContainer, M_DEFAULT, M_FILL_THRESHOLD_X, GenerationInfo.FillXThreshold);
   M3dmapControl(PointCloudContainer, M_DEFAULT, M_FILL_THRESHOLD_Y, GenerationInfo.FillYThreshold);

   if (GenerationInfo.SetExtractOverlap)
      M3dmapControl(PointCloudContainer, M_GENERAL, M_EXTRACTION_OVERLAP, GenerationInfo.ExtractOverlap);

   // Generate the depth map.
   M3dmapExtract(PointCloudContainer, OutDepthmap, M_NULL, M_CORRECTED_DEPTH_MAP, M_ALL, M_DEFAULT);
   }

//****************************************************************************
// PrintResultTable. Prints the result table of all the planarity measures.
//****************************************************************************
void PrintResultTable(const vector<SPlanarityMeasure>& rPlanarityMeasures)
   {
   MosPrintf(MIL_TEXT("|--------------------------------------------------------------|-----------|\n")
             MIL_TEXT("|                 Planarity measure description                | Planarity |\n")
             MIL_TEXT("|--------------------------------------------------------------|-----------|\n"));

   for(MIL_UINT m = 0; m < rPlanarityMeasures.size(); m++)
      {
      MIL_INT NameSize = MosStrlen(rPlanarityMeasures[m].MeasureName);
      MIL_INT Left = (62 - NameSize) / 2;
      MIL_INT Right = 62 - NameSize - Left;
      MosPrintf(MIL_TEXT("|%*s%*s|%8.4f mm|\n"),
                Left + NameSize, rPlanarityMeasures[m].MeasureName,
                Right, MIL_TEXT(""),
                rPlanarityMeasures[m].Planarity);
      }

   MosPrintf(MIL_TEXT("|--------------------------------------------------------------|-----------|\n"));

   }

//****************************************************************************
// CheckForRequiredMILFile. Check for required files to run the example.    
//****************************************************************************
bool CheckForRequiredMILFile(MIL_CONST_TEXT_PTR FileName)
   {
   MIL_INT FilePresent;

   MappFileOperation(M_DEFAULT, FileName, M_NULL, M_NULL, M_FILE_EXISTS, M_DEFAULT, &FilePresent);
   if (FilePresent == M_NO)
      {
      MosPrintf(MIL_TEXT("\n")
                MIL_TEXT("The footage needed to run this example is missing. You need \n")
                MIL_TEXT("to obtain and apply a separate specific update to have it.\n\n"));

      MosPrintf(MIL_TEXT("Press <Enter> to end.\n\n"));
      MosGetch();
      }

   return (FilePresent == M_YES);
   }